]>
Commit | Line | Data |
---|---|---|
d35eef1b EP |
1 | /* |
2 | * 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net> | |
3 | * All rights reserved. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | */ | |
15 | ||
16 | #include <linux/fsnotify.h> | |
17 | #include <linux/jhash.h> | |
18 | #include <linux/in.h> | |
19 | #include <linux/in6.h> | |
20 | #include <linux/kthread.h> | |
21 | #include <linux/pagemap.h> | |
22 | #include <linux/poll.h> | |
5a0e3ad6 | 23 | #include <linux/slab.h> |
d35eef1b EP |
24 | #include <linux/swap.h> |
25 | #include <linux/syscalls.h> | |
898a6cbe | 26 | #include <linux/vmalloc.h> |
d35eef1b EP |
27 | |
28 | #include "netfs.h" | |
29 | ||
d35eef1b EP |
30 | /* |
31 | * Async machinery lives here. | |
32 | * All commands being sent to server do _not_ require sync reply, | |
33 | * instead, if it is really needed, like readdir or readpage, caller | |
34 | * sleeps waiting for data, which will be placed into provided buffer | |
35 | * and caller will be awakened. | |
36 | * | |
37 | * Every command response can come without some listener. For example | |
38 | * readdir response will add new objects into cache without appropriate | |
39 | * request from userspace. This is used in cache coherency. | |
40 | * | |
41 | * If object is not found for given data, it is discarded. | |
42 | * | |
43 | * All requests are received by dedicated kernel thread. | |
44 | */ | |
45 | ||
46 | /* | |
47 | * Basic network sending/receiving functions. | |
48 | * Blocked mode is used. | |
49 | */ | |
50 | static int netfs_data_recv(struct netfs_state *st, void *buf, u64 size) | |
51 | { | |
52 | struct msghdr msg; | |
53 | struct kvec iov; | |
54 | int err; | |
55 | ||
56 | BUG_ON(!size); | |
57 | ||
58 | iov.iov_base = buf; | |
59 | iov.iov_len = size; | |
60 | ||
61 | msg.msg_iov = (struct iovec *)&iov; | |
62 | msg.msg_iovlen = 1; | |
63 | msg.msg_name = NULL; | |
64 | msg.msg_namelen = 0; | |
65 | msg.msg_control = NULL; | |
66 | msg.msg_controllen = 0; | |
67 | msg.msg_flags = MSG_DONTWAIT; | |
68 | ||
69 | err = kernel_recvmsg(st->socket, &msg, &iov, 1, iov.iov_len, | |
70 | msg.msg_flags); | |
71 | if (err <= 0) { | |
72 | printk("%s: failed to recv data: size: %llu, err: %d.\n", __func__, size, err); | |
73 | if (err == 0) | |
74 | err = -ECONNRESET; | |
75 | } | |
76 | ||
77 | return err; | |
78 | } | |
79 | ||
80 | static int pohmelfs_data_recv(struct netfs_state *st, void *data, unsigned int size) | |
81 | { | |
82 | unsigned int revents = 0; | |
83 | unsigned int err_mask = POLLERR | POLLHUP | POLLRDHUP; | |
84 | unsigned int mask = err_mask | POLLIN; | |
85 | int err = 0; | |
86 | ||
87 | while (size && !err) { | |
88 | revents = netfs_state_poll(st); | |
89 | ||
90 | if (!(revents & mask)) { | |
91 | DEFINE_WAIT(wait); | |
92 | ||
93 | for (;;) { | |
94 | prepare_to_wait(&st->thread_wait, &wait, TASK_INTERRUPTIBLE); | |
95 | if (kthread_should_stop()) | |
96 | break; | |
97 | ||
98 | revents = netfs_state_poll(st); | |
99 | ||
100 | if (revents & mask) | |
101 | break; | |
102 | ||
103 | if (signal_pending(current)) | |
104 | break; | |
105 | ||
106 | schedule(); | |
107 | continue; | |
108 | } | |
109 | finish_wait(&st->thread_wait, &wait); | |
110 | } | |
111 | ||
112 | err = 0; | |
113 | netfs_state_lock(st); | |
114 | if (st->socket && (st->read_socket == st->socket) && (revents & POLLIN)) { | |
115 | err = netfs_data_recv(st, data, size); | |
116 | if (err > 0) { | |
117 | data += err; | |
118 | size -= err; | |
119 | err = 0; | |
120 | } else if (err == 0) | |
121 | err = -ECONNRESET; | |
122 | } | |
123 | ||
124 | if (revents & err_mask) { | |
125 | printk("%s: revents: %x, socket: %p, size: %u, err: %d.\n", | |
126 | __func__, revents, st->socket, size, err); | |
127 | err = -ECONNRESET; | |
128 | } | |
129 | netfs_state_unlock(st); | |
130 | ||
131 | if (err < 0) { | |
132 | if (netfs_state_trylock_send(st)) { | |
133 | netfs_state_exit(st); | |
134 | err = netfs_state_init(st); | |
135 | if (!err) | |
136 | err = -EAGAIN; | |
137 | netfs_state_unlock_send(st); | |
138 | } else { | |
139 | st->need_reset = 1; | |
140 | } | |
141 | } | |
142 | ||
143 | if (kthread_should_stop()) | |
144 | err = -ENODEV; | |
145 | ||
146 | if (err) | |
147 | printk("%s: socket: %p, read_socket: %p, revents: %x, rev_error: %d, " | |
148 | "should_stop: %d, size: %u, err: %d.\n", | |
149 | __func__, st->socket, st->read_socket, | |
150 | revents, revents & err_mask, kthread_should_stop(), size, err); | |
151 | } | |
152 | ||
153 | return err; | |
154 | } | |
155 | ||
156 | int pohmelfs_data_recv_and_check(struct netfs_state *st, void *data, unsigned int size) | |
157 | { | |
158 | struct netfs_cmd *cmd = &st->cmd; | |
159 | int err; | |
160 | ||
161 | err = pohmelfs_data_recv(st, data, size); | |
162 | if (err) | |
163 | return err; | |
164 | ||
165 | return pohmelfs_crypto_process_input_data(&st->eng, cmd->iv, data, NULL, size); | |
166 | } | |
167 | ||
168 | /* | |
169 | * Polling machinery. | |
170 | */ | |
171 | ||
385e3f1a | 172 | struct netfs_poll_helper { |
d35eef1b EP |
173 | poll_table pt; |
174 | struct netfs_state *st; | |
175 | }; | |
176 | ||
177 | static int netfs_queue_wake(wait_queue_t *wait, unsigned mode, int sync, void *key) | |
178 | { | |
179 | struct netfs_state *st = container_of(wait, struct netfs_state, wait); | |
180 | ||
181 | wake_up(&st->thread_wait); | |
182 | return 1; | |
183 | } | |
184 | ||
185 | static void netfs_queue_func(struct file *file, wait_queue_head_t *whead, | |
186 | poll_table *pt) | |
187 | { | |
188 | struct netfs_state *st = container_of(pt, struct netfs_poll_helper, pt)->st; | |
189 | ||
190 | st->whead = whead; | |
191 | init_waitqueue_func_entry(&st->wait, netfs_queue_wake); | |
192 | add_wait_queue(whead, &st->wait); | |
193 | } | |
194 | ||
195 | static void netfs_poll_exit(struct netfs_state *st) | |
196 | { | |
197 | if (st->whead) { | |
198 | remove_wait_queue(st->whead, &st->wait); | |
199 | st->whead = NULL; | |
200 | } | |
201 | } | |
202 | ||
203 | static int netfs_poll_init(struct netfs_state *st) | |
204 | { | |
205 | struct netfs_poll_helper ph; | |
206 | ||
207 | ph.st = st; | |
208 | init_poll_funcptr(&ph.pt, &netfs_queue_func); | |
209 | ||
210 | st->socket->ops->poll(NULL, st->socket, &ph.pt); | |
211 | return 0; | |
212 | } | |
213 | ||
214 | /* | |
215 | * Get response for readpage command. We search inode and page in its mapping | |
216 | * and copy data into. If it was async request, then we queue page into shared | |
217 | * data and wakeup listener, who will copy it to userspace. | |
218 | * | |
219 | * There is a work in progress of allowing to call copy_to_user() directly from | |
220 | * async receiving kernel thread. | |
221 | */ | |
222 | static int pohmelfs_read_page_response(struct netfs_state *st) | |
223 | { | |
224 | struct pohmelfs_sb *psb = st->psb; | |
225 | struct netfs_cmd *cmd = &st->cmd; | |
226 | struct inode *inode; | |
227 | struct page *page; | |
228 | int err = 0; | |
229 | ||
230 | if (cmd->size > PAGE_CACHE_SIZE) { | |
231 | err = -EINVAL; | |
232 | goto err_out_exit; | |
233 | } | |
234 | ||
235 | inode = ilookup(st->psb->sb, cmd->id); | |
236 | if (!inode) { | |
237 | printk("%s: failed to find inode: id: %llu.\n", __func__, cmd->id); | |
238 | err = -ENOENT; | |
239 | goto err_out_exit; | |
240 | } | |
241 | ||
242 | page = find_get_page(inode->i_mapping, cmd->start >> PAGE_CACHE_SHIFT); | |
243 | if (!page || !PageLocked(page)) { | |
244 | printk("%s: failed to find/lock page: page: %p, id: %llu, start: %llu, index: %llu.\n", | |
245 | __func__, page, cmd->id, cmd->start, cmd->start >> PAGE_CACHE_SHIFT); | |
246 | ||
247 | while (cmd->size) { | |
248 | unsigned int sz = min(cmd->size, st->size); | |
249 | ||
250 | err = pohmelfs_data_recv(st, st->data, sz); | |
251 | if (err) | |
252 | break; | |
253 | ||
254 | cmd->size -= sz; | |
255 | } | |
256 | ||
257 | err = -ENODEV; | |
258 | if (page) | |
259 | goto err_out_page_put; | |
260 | goto err_out_put; | |
261 | } | |
262 | ||
263 | if (cmd->size) { | |
264 | void *addr; | |
265 | ||
266 | addr = kmap(page); | |
267 | err = pohmelfs_data_recv(st, addr, cmd->size); | |
268 | kunmap(page); | |
269 | ||
270 | if (err) | |
271 | goto err_out_page_unlock; | |
272 | } | |
273 | ||
274 | dprintk("%s: page: %p, start: %llu, size: %u, locked: %d.\n", | |
275 | __func__, page, cmd->start, cmd->size, PageLocked(page)); | |
276 | ||
277 | SetPageChecked(page); | |
278 | if ((psb->hash_string || psb->cipher_string) && psb->perform_crypto && cmd->size) { | |
279 | err = pohmelfs_crypto_process_input_page(&st->eng, page, cmd->size, cmd->iv); | |
280 | if (err < 0) | |
281 | goto err_out_page_unlock; | |
282 | } else { | |
283 | SetPageUptodate(page); | |
284 | unlock_page(page); | |
285 | page_cache_release(page); | |
286 | } | |
287 | ||
288 | pohmelfs_put_inode(POHMELFS_I(inode)); | |
289 | wake_up(&st->psb->wait); | |
290 | ||
291 | return 0; | |
292 | ||
293 | err_out_page_unlock: | |
294 | SetPageError(page); | |
295 | unlock_page(page); | |
296 | err_out_page_put: | |
297 | page_cache_release(page); | |
298 | err_out_put: | |
299 | pohmelfs_put_inode(POHMELFS_I(inode)); | |
300 | err_out_exit: | |
301 | wake_up(&st->psb->wait); | |
302 | return err; | |
303 | } | |
304 | ||
305 | static int pohmelfs_check_name(struct pohmelfs_inode *parent, struct qstr *str, | |
306 | struct netfs_inode_info *info) | |
307 | { | |
308 | struct inode *inode; | |
309 | struct pohmelfs_name *n; | |
310 | int err = 0; | |
311 | u64 ino = 0; | |
312 | ||
313 | mutex_lock(&parent->offset_lock); | |
314 | n = pohmelfs_search_hash(parent, str->hash); | |
315 | if (n) | |
316 | ino = n->ino; | |
317 | mutex_unlock(&parent->offset_lock); | |
318 | ||
319 | if (!ino) | |
320 | goto out; | |
321 | ||
322 | inode = ilookup(parent->vfs_inode.i_sb, ino); | |
323 | if (!inode) | |
324 | goto out; | |
325 | ||
326 | dprintk("%s: parent: %llu, inode: %llu.\n", __func__, parent->ino, ino); | |
327 | ||
328 | pohmelfs_fill_inode(inode, info); | |
329 | pohmelfs_put_inode(POHMELFS_I(inode)); | |
330 | err = -EEXIST; | |
331 | out: | |
332 | return err; | |
333 | } | |
334 | ||
335 | /* | |
336 | * Readdir response from server. If special field is set, we wakeup | |
337 | * listener (readdir() call), which will copy data to userspace. | |
338 | */ | |
339 | static int pohmelfs_readdir_response(struct netfs_state *st) | |
340 | { | |
341 | struct inode *inode; | |
342 | struct netfs_cmd *cmd = &st->cmd; | |
343 | struct netfs_inode_info *info; | |
344 | struct pohmelfs_inode *parent = NULL, *npi; | |
345 | int err = 0, last = cmd->ext; | |
346 | struct qstr str; | |
347 | ||
348 | if (cmd->size > st->size) | |
349 | return -EINVAL; | |
350 | ||
351 | inode = ilookup(st->psb->sb, cmd->id); | |
352 | if (!inode) { | |
353 | printk("%s: failed to find inode: id: %llu.\n", __func__, cmd->id); | |
354 | return -ENOENT; | |
355 | } | |
356 | parent = POHMELFS_I(inode); | |
357 | ||
358 | if (!cmd->size && cmd->start) { | |
359 | err = -cmd->start; | |
360 | goto out; | |
361 | } | |
362 | ||
363 | if (cmd->size) { | |
364 | char *name; | |
365 | ||
366 | err = pohmelfs_data_recv_and_check(st, st->data, cmd->size); | |
367 | if (err) | |
368 | goto err_out_put; | |
369 | ||
370 | info = (struct netfs_inode_info *)(st->data); | |
371 | ||
372 | name = (char *)(info + 1); | |
373 | str.len = cmd->size - sizeof(struct netfs_inode_info) - 1 - cmd->cpad; | |
374 | name[str.len] = 0; | |
375 | str.name = name; | |
376 | str.hash = jhash(str.name, str.len, 0); | |
377 | ||
378 | netfs_convert_inode_info(info); | |
379 | ||
380 | if (parent) { | |
381 | err = pohmelfs_check_name(parent, &str, info); | |
382 | if (err) { | |
383 | if (err == -EEXIST) | |
384 | err = 0; | |
385 | goto out; | |
386 | } | |
387 | } | |
388 | ||
389 | info->ino = cmd->start; | |
390 | if (!info->ino) | |
391 | info->ino = pohmelfs_new_ino(st->psb); | |
392 | ||
393 | dprintk("%s: parent: %llu, ino: %llu, name: '%s', hash: %x, len: %u, mode: %o.\n", | |
394 | __func__, parent->ino, info->ino, str.name, str.hash, str.len, | |
395 | info->mode); | |
396 | ||
397 | npi = pohmelfs_new_inode(st->psb, parent, &str, info, 0); | |
398 | if (IS_ERR(npi)) { | |
399 | err = PTR_ERR(npi); | |
400 | ||
401 | if (err != -EEXIST) | |
402 | goto err_out_put; | |
403 | } else { | |
288a9a89 EP |
404 | struct dentry *dentry, *alias, *pd; |
405 | ||
d35eef1b EP |
406 | set_bit(NETFS_INODE_REMOTE_SYNCED, &npi->state); |
407 | clear_bit(NETFS_INODE_OWNED, &npi->state); | |
288a9a89 EP |
408 | |
409 | pd = d_find_alias(&parent->vfs_inode); | |
410 | if (pd) { | |
411 | str.hash = full_name_hash(str.name, str.len); | |
412 | dentry = d_alloc(pd, &str); | |
413 | if (dentry) { | |
414 | alias = d_materialise_unique(dentry, &npi->vfs_inode); | |
415 | if (alias) | |
416 | dput(dentry); | |
417 | } | |
418 | ||
419 | dput(dentry); | |
420 | dput(pd); | |
421 | } | |
d35eef1b EP |
422 | } |
423 | } | |
424 | out: | |
425 | if (last) { | |
426 | set_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &parent->state); | |
427 | set_bit(NETFS_INODE_REMOTE_SYNCED, &parent->state); | |
428 | wake_up(&st->psb->wait); | |
429 | } | |
430 | pohmelfs_put_inode(parent); | |
431 | ||
432 | return err; | |
433 | ||
434 | err_out_put: | |
435 | clear_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &parent->state); | |
436 | printk("%s: parent: %llu, ino: %llu, cmd_id: %llu.\n", __func__, parent->ino, cmd->start, cmd->id); | |
437 | pohmelfs_put_inode(parent); | |
438 | wake_up(&st->psb->wait); | |
439 | return err; | |
440 | } | |
441 | ||
442 | /* | |
443 | * Lookup command response. | |
444 | * It searches for inode to be looked at (if it exists) and substitutes | |
445 | * its inode information (size, permission, mode and so on), if inode does | |
446 | * not exist, new one will be created and inserted into caches. | |
447 | */ | |
448 | static int pohmelfs_lookup_response(struct netfs_state *st) | |
449 | { | |
450 | struct inode *inode = NULL; | |
451 | struct netfs_cmd *cmd = &st->cmd; | |
452 | struct netfs_inode_info *info; | |
453 | struct pohmelfs_inode *parent = NULL, *npi; | |
454 | int err = -EINVAL; | |
455 | char *name; | |
456 | ||
457 | inode = ilookup(st->psb->sb, cmd->id); | |
458 | if (!inode) { | |
459 | printk("%s: lookup response: id: %llu, start: %llu, size: %u.\n", | |
460 | __func__, cmd->id, cmd->start, cmd->size); | |
461 | err = -ENOENT; | |
462 | goto err_out_exit; | |
463 | } | |
464 | parent = POHMELFS_I(inode); | |
465 | ||
466 | if (!cmd->size) { | |
467 | err = -cmd->start; | |
468 | goto err_out_put; | |
469 | } | |
470 | ||
471 | if (cmd->size < sizeof(struct netfs_inode_info)) { | |
472 | printk("%s: broken lookup response: id: %llu, start: %llu, size: %u.\n", | |
473 | __func__, cmd->id, cmd->start, cmd->size); | |
474 | err = -EINVAL; | |
475 | goto err_out_put; | |
476 | } | |
477 | ||
478 | err = pohmelfs_data_recv_and_check(st, st->data, cmd->size); | |
479 | if (err) | |
480 | goto err_out_put; | |
481 | ||
482 | info = (struct netfs_inode_info *)(st->data); | |
483 | name = (char *)(info + 1); | |
484 | ||
485 | netfs_convert_inode_info(info); | |
486 | ||
487 | info->ino = cmd->start; | |
488 | if (!info->ino) | |
489 | info->ino = pohmelfs_new_ino(st->psb); | |
490 | ||
491 | dprintk("%s: parent: %llu, ino: %llu, name: '%s', start: %llu.\n", | |
492 | __func__, parent->ino, info->ino, name, cmd->start); | |
493 | ||
494 | if (cmd->start) | |
495 | npi = pohmelfs_new_inode(st->psb, parent, NULL, info, 0); | |
496 | else { | |
497 | struct qstr str; | |
498 | ||
499 | str.name = name; | |
500 | str.len = cmd->size - sizeof(struct netfs_inode_info) - 1 - cmd->cpad; | |
501 | str.hash = jhash(name, str.len, 0); | |
502 | ||
503 | npi = pohmelfs_new_inode(st->psb, parent, &str, info, 0); | |
504 | } | |
505 | if (IS_ERR(npi)) { | |
506 | err = PTR_ERR(npi); | |
507 | ||
508 | if (err != -EEXIST) | |
509 | goto err_out_put; | |
510 | } else { | |
511 | set_bit(NETFS_INODE_REMOTE_SYNCED, &npi->state); | |
512 | clear_bit(NETFS_INODE_OWNED, &npi->state); | |
513 | } | |
514 | ||
515 | clear_bit(NETFS_COMMAND_PENDING, &parent->state); | |
516 | pohmelfs_put_inode(parent); | |
517 | ||
518 | wake_up(&st->psb->wait); | |
519 | ||
520 | return 0; | |
521 | ||
522 | err_out_put: | |
523 | pohmelfs_put_inode(parent); | |
524 | err_out_exit: | |
525 | clear_bit(NETFS_COMMAND_PENDING, &parent->state); | |
526 | wake_up(&st->psb->wait); | |
527 | printk("%s: inode: %p, id: %llu, start: %llu, size: %u, err: %d.\n", | |
528 | __func__, inode, cmd->id, cmd->start, cmd->size, err); | |
529 | return err; | |
530 | } | |
531 | ||
532 | /* | |
533 | * Create response, just marks local inode as 'created', so that writeback | |
534 | * for any of its children (or own) would not try to sync it again. | |
535 | */ | |
536 | static int pohmelfs_create_response(struct netfs_state *st) | |
537 | { | |
538 | struct inode *inode; | |
539 | struct netfs_cmd *cmd = &st->cmd; | |
540 | struct pohmelfs_inode *pi; | |
541 | ||
542 | inode = ilookup(st->psb->sb, cmd->id); | |
543 | if (!inode) { | |
544 | printk("%s: failed to find inode: id: %llu, start: %llu.\n", | |
545 | __func__, cmd->id, cmd->start); | |
546 | goto err_out_exit; | |
547 | } | |
548 | ||
549 | pi = POHMELFS_I(inode); | |
550 | ||
551 | /* | |
552 | * To lock or not to lock? | |
553 | * We actually do not care if it races... | |
554 | */ | |
555 | if (cmd->start) | |
556 | make_bad_inode(inode); | |
557 | set_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state); | |
558 | ||
559 | pohmelfs_put_inode(pi); | |
560 | ||
561 | wake_up(&st->psb->wait); | |
562 | return 0; | |
563 | ||
564 | err_out_exit: | |
565 | wake_up(&st->psb->wait); | |
566 | return -ENOENT; | |
567 | } | |
568 | ||
569 | /* | |
570 | * Object remove response. Just says that remove request has been received. | |
571 | * Used in cache coherency protocol. | |
572 | */ | |
573 | static int pohmelfs_remove_response(struct netfs_state *st) | |
574 | { | |
575 | struct netfs_cmd *cmd = &st->cmd; | |
576 | int err; | |
577 | ||
578 | err = pohmelfs_data_recv_and_check(st, st->data, cmd->size); | |
579 | if (err) | |
580 | return err; | |
581 | ||
582 | dprintk("%s: parent: %llu, path: '%s'.\n", __func__, cmd->id, (char *)st->data); | |
583 | ||
584 | return 0; | |
585 | } | |
586 | ||
587 | /* | |
588 | * Transaction reply processing. | |
589 | * | |
590 | * Find transaction based on its generation number, bump its reference counter, | |
591 | * so that none could free it under us, drop from the trees and lists and | |
592 | * drop reference counter. When it hits zero (when all destinations replied | |
593 | * and all timeout handled by async scanning code), completion will be called | |
594 | * and transaction will be freed. | |
595 | */ | |
596 | static int pohmelfs_transaction_response(struct netfs_state *st) | |
597 | { | |
598 | struct netfs_trans_dst *dst; | |
599 | struct netfs_trans *t = NULL; | |
600 | struct netfs_cmd *cmd = &st->cmd; | |
601 | short err = (signed)cmd->ext; | |
602 | ||
603 | mutex_lock(&st->trans_lock); | |
604 | dst = netfs_trans_search(st, cmd->start); | |
605 | if (dst) { | |
606 | netfs_trans_remove_nolock(dst, st); | |
607 | t = dst->trans; | |
d35eef1b EP |
608 | } |
609 | mutex_unlock(&st->trans_lock); | |
610 | ||
611 | if (!t) { | |
50e4babf EP |
612 | printk("%s: failed to find transaction: start: %llu: id: %llu, size: %u, ext: %u.\n", |
613 | __func__, cmd->start, cmd->id, cmd->size, cmd->ext); | |
d35eef1b EP |
614 | err = -EINVAL; |
615 | goto out; | |
616 | } | |
617 | ||
618 | t->result = err; | |
619 | netfs_trans_drop_dst_nostate(dst); | |
620 | ||
621 | out: | |
622 | wake_up(&st->psb->wait); | |
623 | return err; | |
624 | } | |
625 | ||
626 | /* | |
627 | * Inode metadata cache coherency message. | |
628 | */ | |
629 | static int pohmelfs_page_cache_response(struct netfs_state *st) | |
630 | { | |
631 | struct netfs_cmd *cmd = &st->cmd; | |
632 | struct inode *inode; | |
633 | ||
634 | dprintk("%s: st: %p, id: %llu, start: %llu, size: %u.\n", __func__, st, cmd->id, cmd->start, cmd->size); | |
635 | ||
636 | inode = ilookup(st->psb->sb, cmd->id); | |
637 | if (!inode) { | |
638 | printk("%s: failed to find inode: id: %llu.\n", __func__, cmd->id); | |
639 | return -ENOENT; | |
640 | } | |
641 | ||
642 | set_bit(NETFS_INODE_NEED_FLUSH, &POHMELFS_I(inode)->state); | |
643 | pohmelfs_put_inode(POHMELFS_I(inode)); | |
644 | ||
645 | return 0; | |
646 | } | |
647 | ||
648 | /* | |
649 | * Root capabilities response: export statistics | |
650 | * like used and available size, number of files and dirs, | |
651 | * permissions. | |
652 | */ | |
653 | static int pohmelfs_root_cap_response(struct netfs_state *st) | |
654 | { | |
655 | struct netfs_cmd *cmd = &st->cmd; | |
656 | struct netfs_root_capabilities *cap; | |
657 | struct pohmelfs_sb *psb = st->psb; | |
658 | ||
659 | if (cmd->size != sizeof(struct netfs_root_capabilities)) { | |
660 | psb->flags = EPROTO; | |
661 | wake_up(&psb->wait); | |
662 | return -EPROTO; | |
663 | } | |
664 | ||
665 | cap = st->data; | |
666 | ||
667 | netfs_convert_root_capabilities(cap); | |
668 | ||
669 | if (psb->total_size < cap->used + cap->avail) | |
670 | psb->total_size = cap->used + cap->avail; | |
671 | if (cap->avail) | |
672 | psb->avail_size = cap->avail; | |
673 | psb->state_flags = cap->flags; | |
674 | ||
675 | if (psb->state_flags & POHMELFS_FLAGS_RO) { | |
676 | psb->sb->s_flags |= MS_RDONLY; | |
677 | printk(KERN_INFO "Mounting POHMELFS (%d) read-only.\n", psb->idx); | |
678 | } | |
679 | ||
680 | if (psb->state_flags & POHMELFS_FLAGS_XATTR) | |
681 | printk(KERN_INFO "Mounting POHMELFS (%d) " | |
682 | "with extended attributes support.\n", psb->idx); | |
683 | ||
76efa5e3 | 684 | if (atomic_long_read(&psb->total_inodes) <= 1) |
d35eef1b EP |
685 | atomic_long_set(&psb->total_inodes, cap->nr_files); |
686 | ||
687 | dprintk("%s: total: %llu, avail: %llu, flags: %llx, inodes: %llu.\n", | |
688 | __func__, psb->total_size, psb->avail_size, psb->state_flags, cap->nr_files); | |
689 | ||
690 | psb->flags = 0; | |
691 | wake_up(&psb->wait); | |
692 | return 0; | |
693 | } | |
694 | ||
695 | /* | |
696 | * Crypto capabilities of the server, where it says that | |
697 | * it supports or does not requested hash/cipher algorithms. | |
698 | */ | |
699 | static int pohmelfs_crypto_cap_response(struct netfs_state *st) | |
700 | { | |
701 | struct netfs_cmd *cmd = &st->cmd; | |
702 | struct netfs_crypto_capabilities *cap; | |
703 | struct pohmelfs_sb *psb = st->psb; | |
704 | int err = 0; | |
705 | ||
706 | if (cmd->size != sizeof(struct netfs_crypto_capabilities)) { | |
707 | psb->flags = EPROTO; | |
708 | wake_up(&psb->wait); | |
709 | return -EPROTO; | |
710 | } | |
711 | ||
712 | cap = st->data; | |
713 | ||
714 | dprintk("%s: cipher '%s': %s, hash: '%s': %s.\n", | |
715 | __func__, | |
716 | psb->cipher_string, (cap->cipher_strlen)?"SUPPORTED":"NOT SUPPORTED", | |
717 | psb->hash_string, (cap->hash_strlen)?"SUPPORTED":"NOT SUPPORTED"); | |
718 | ||
719 | if (!cap->hash_strlen) { | |
720 | if (psb->hash_strlen && psb->crypto_fail_unsupported) | |
721 | err = -ENOTSUPP; | |
722 | psb->hash_strlen = 0; | |
723 | kfree(psb->hash_string); | |
724 | psb->hash_string = NULL; | |
725 | } | |
726 | ||
727 | if (!cap->cipher_strlen) { | |
728 | if (psb->cipher_strlen && psb->crypto_fail_unsupported) | |
729 | err = -ENOTSUPP; | |
730 | psb->cipher_strlen = 0; | |
731 | kfree(psb->cipher_string); | |
732 | psb->cipher_string = NULL; | |
733 | } | |
734 | ||
735 | return err; | |
736 | } | |
737 | ||
738 | /* | |
739 | * Capabilities handshake response. | |
740 | */ | |
741 | static int pohmelfs_capabilities_response(struct netfs_state *st) | |
742 | { | |
743 | struct netfs_cmd *cmd = &st->cmd; | |
744 | int err = 0; | |
745 | ||
746 | err = pohmelfs_data_recv(st, st->data, cmd->size); | |
747 | if (err) | |
748 | return err; | |
749 | ||
750 | switch (cmd->id) { | |
751 | case POHMELFS_CRYPTO_CAPABILITIES: | |
752 | return pohmelfs_crypto_cap_response(st); | |
753 | case POHMELFS_ROOT_CAPABILITIES: | |
754 | return pohmelfs_root_cap_response(st); | |
755 | default: | |
756 | break; | |
757 | } | |
758 | return -EINVAL; | |
759 | } | |
760 | ||
761 | /* | |
762 | * Receiving extended attribute. | |
763 | * Does not work properly if received size is more than requested one, | |
764 | * it should not happen with current request/reply model though. | |
765 | */ | |
766 | static int pohmelfs_getxattr_response(struct netfs_state *st) | |
767 | { | |
768 | struct pohmelfs_sb *psb = st->psb; | |
769 | struct netfs_cmd *cmd = &st->cmd; | |
770 | struct pohmelfs_mcache *m; | |
771 | short error = (signed short)cmd->ext, err; | |
772 | unsigned int sz, total_size; | |
773 | ||
774 | m = pohmelfs_mcache_search(psb, cmd->id); | |
775 | ||
776 | dprintk("%s: id: %llu, gen: %llu, err: %d.\n", | |
777 | __func__, cmd->id, (m)?m->gen:0, error); | |
778 | ||
779 | if (!m) { | |
780 | printk("%s: failed to find getxattr cache entry: id: %llu.\n", __func__, cmd->id); | |
781 | return -ENOENT; | |
782 | } | |
783 | ||
784 | if (cmd->size) { | |
785 | sz = min_t(unsigned int, cmd->size, m->size); | |
786 | err = pohmelfs_data_recv_and_check(st, m->data, sz); | |
787 | if (err) { | |
788 | error = err; | |
789 | goto out; | |
790 | } | |
791 | ||
792 | m->size = sz; | |
793 | total_size = cmd->size - sz; | |
794 | ||
795 | while (total_size) { | |
796 | sz = min(total_size, st->size); | |
797 | ||
798 | err = pohmelfs_data_recv_and_check(st, st->data, sz); | |
799 | if (err) { | |
800 | error = err; | |
801 | break; | |
802 | } | |
803 | ||
804 | total_size -= sz; | |
805 | } | |
806 | } | |
807 | ||
808 | out: | |
809 | m->err = error; | |
810 | complete(&m->complete); | |
811 | pohmelfs_mcache_put(psb, m); | |
812 | ||
813 | return error; | |
814 | } | |
815 | ||
816 | int pohmelfs_data_lock_response(struct netfs_state *st) | |
817 | { | |
818 | struct pohmelfs_sb *psb = st->psb; | |
819 | struct netfs_cmd *cmd = &st->cmd; | |
820 | struct pohmelfs_mcache *m; | |
821 | short err = (signed short)cmd->ext; | |
822 | u64 id = cmd->id; | |
823 | ||
824 | m = pohmelfs_mcache_search(psb, id); | |
825 | ||
826 | dprintk("%s: id: %llu, gen: %llu, err: %d.\n", | |
827 | __func__, cmd->id, (m)?m->gen:0, err); | |
828 | ||
829 | if (!m) { | |
830 | pohmelfs_data_recv(st, st->data, cmd->size); | |
831 | printk("%s: failed to find data lock response: id: %llu.\n", __func__, cmd->id); | |
832 | return -ENOENT; | |
833 | } | |
834 | ||
835 | if (cmd->size) | |
836 | err = pohmelfs_data_recv_and_check(st, &m->info, cmd->size); | |
837 | ||
838 | m->err = err; | |
839 | complete(&m->complete); | |
840 | pohmelfs_mcache_put(psb, m); | |
841 | ||
842 | return err; | |
843 | } | |
844 | ||
845 | static void __inline__ netfs_state_reset(struct netfs_state *st) | |
846 | { | |
847 | netfs_state_lock_send(st); | |
848 | netfs_state_exit(st); | |
849 | netfs_state_init(st); | |
850 | netfs_state_unlock_send(st); | |
851 | } | |
852 | ||
853 | /* | |
854 | * Main receiving function, called from dedicated kernel thread. | |
855 | */ | |
856 | static int pohmelfs_recv(void *data) | |
857 | { | |
858 | int err = -EINTR; | |
859 | struct netfs_state *st = data; | |
860 | struct netfs_cmd *cmd = &st->cmd; | |
861 | ||
862 | while (!kthread_should_stop()) { | |
863 | /* | |
864 | * If socket will be reset after this statement, then | |
865 | * pohmelfs_data_recv() will just fail and loop will | |
866 | * start again, so it can be done without any locks. | |
867 | * | |
868 | * st->read_socket is needed to prevents state machine | |
869 | * breaking between this data reading and subsequent one | |
870 | * in protocol specific functions during connection reset. | |
871 | * In case of reset we have to read next command and do | |
872 | * not expect data for old command to magically appear in | |
873 | * new connection. | |
874 | */ | |
875 | st->read_socket = st->socket; | |
876 | err = pohmelfs_data_recv(st, cmd, sizeof(struct netfs_cmd)); | |
877 | if (err) { | |
878 | msleep(1000); | |
879 | continue; | |
880 | } | |
881 | ||
882 | netfs_convert_cmd(cmd); | |
883 | ||
884 | dprintk("%s: cmd: %u, id: %llu, start: %llu, size: %u, " | |
885 | "ext: %u, csize: %u, cpad: %u.\n", | |
886 | __func__, cmd->cmd, cmd->id, cmd->start, | |
887 | cmd->size, cmd->ext, cmd->csize, cmd->cpad); | |
888 | ||
889 | if (cmd->csize) { | |
890 | struct pohmelfs_crypto_engine *e = &st->eng; | |
891 | ||
892 | if (unlikely(cmd->csize > e->size/2)) { | |
893 | netfs_state_reset(st); | |
894 | continue; | |
895 | } | |
896 | ||
897 | if (e->hash && unlikely(cmd->csize != st->psb->crypto_attached_size)) { | |
898 | dprintk("%s: cmd: cmd: %u, id: %llu, start: %llu, size: %u, " | |
899 | "csize: %u != digest size %u.\n", | |
900 | __func__, cmd->cmd, cmd->id, cmd->start, cmd->size, | |
901 | cmd->csize, st->psb->crypto_attached_size); | |
902 | netfs_state_reset(st); | |
903 | continue; | |
904 | } | |
905 | ||
906 | err = pohmelfs_data_recv(st, e->data, cmd->csize); | |
907 | if (err) { | |
908 | netfs_state_reset(st); | |
909 | continue; | |
910 | } | |
911 | ||
912 | #ifdef CONFIG_POHMELFS_DEBUG | |
913 | { | |
914 | unsigned int i; | |
915 | unsigned char *hash = e->data; | |
916 | ||
917 | dprintk("%s: received hash: ", __func__); | |
dc828461 | 918 | for (i=0; i<cmd->csize; ++i) |
d35eef1b | 919 | printk("%02x ", hash[i]); |
dc828461 | 920 | |
d35eef1b EP |
921 | printk("\n"); |
922 | } | |
923 | #endif | |
924 | cmd->size -= cmd->csize; | |
925 | } | |
926 | ||
927 | /* | |
928 | * This should catch protocol breakage and random garbage instead of commands. | |
929 | */ | |
930 | if (unlikely((cmd->size > st->size) && (cmd->cmd != NETFS_XATTR_GET))) { | |
931 | netfs_state_reset(st); | |
932 | continue; | |
933 | } | |
934 | ||
935 | switch (cmd->cmd) { | |
936 | case NETFS_READ_PAGE: | |
937 | err = pohmelfs_read_page_response(st); | |
938 | break; | |
939 | case NETFS_READDIR: | |
940 | err = pohmelfs_readdir_response(st); | |
941 | break; | |
942 | case NETFS_LOOKUP: | |
943 | err = pohmelfs_lookup_response(st); | |
944 | break; | |
945 | case NETFS_CREATE: | |
946 | err = pohmelfs_create_response(st); | |
947 | break; | |
948 | case NETFS_REMOVE: | |
949 | err = pohmelfs_remove_response(st); | |
950 | break; | |
951 | case NETFS_TRANS: | |
952 | err = pohmelfs_transaction_response(st); | |
953 | break; | |
954 | case NETFS_PAGE_CACHE: | |
955 | err = pohmelfs_page_cache_response(st); | |
956 | break; | |
957 | case NETFS_CAPABILITIES: | |
958 | err = pohmelfs_capabilities_response(st); | |
959 | break; | |
960 | case NETFS_LOCK: | |
961 | err = pohmelfs_data_lock_response(st); | |
962 | break; | |
963 | case NETFS_XATTR_GET: | |
964 | err = pohmelfs_getxattr_response(st); | |
965 | break; | |
966 | default: | |
967 | printk("%s: wrong cmd: %u, id: %llu, start: %llu, size: %u, ext: %u.\n", | |
968 | __func__, cmd->cmd, cmd->id, cmd->start, cmd->size, cmd->ext); | |
969 | netfs_state_reset(st); | |
970 | break; | |
971 | } | |
972 | } | |
973 | ||
974 | while (!kthread_should_stop()) | |
975 | schedule_timeout_uninterruptible(msecs_to_jiffies(10)); | |
976 | ||
977 | return err; | |
978 | } | |
979 | ||
980 | int netfs_state_init(struct netfs_state *st) | |
981 | { | |
982 | int err; | |
983 | struct pohmelfs_ctl *ctl = &st->ctl; | |
984 | ||
985 | err = sock_create(ctl->addr.sa_family, ctl->type, ctl->proto, &st->socket); | |
986 | if (err) { | |
987 | printk("%s: failed to create a socket: family: %d, type: %d, proto: %d, err: %d.\n", | |
988 | __func__, ctl->addr.sa_family, ctl->type, ctl->proto, err); | |
989 | goto err_out_exit; | |
990 | } | |
991 | ||
992 | st->socket->sk->sk_allocation = GFP_NOIO; | |
993 | st->socket->sk->sk_sndtimeo = st->socket->sk->sk_rcvtimeo = msecs_to_jiffies(60000); | |
994 | ||
995 | err = kernel_connect(st->socket, (struct sockaddr *)&ctl->addr, ctl->addrlen, 0); | |
996 | if (err) { | |
997 | printk("%s: failed to connect to server: idx: %u, err: %d.\n", | |
998 | __func__, st->psb->idx, err); | |
999 | goto err_out_release; | |
1000 | } | |
1001 | st->socket->sk->sk_sndtimeo = st->socket->sk->sk_rcvtimeo = msecs_to_jiffies(60000); | |
1002 | ||
1003 | err = netfs_poll_init(st); | |
1004 | if (err) | |
1005 | goto err_out_release; | |
1006 | ||
1007 | if (st->socket->ops->family == AF_INET) { | |
1008 | struct sockaddr_in *sin = (struct sockaddr_in *)&ctl->addr; | |
2d7cf8ef EP |
1009 | printk(KERN_INFO "%s: (re)connected to peer %pi4:%d.\n", __func__, |
1010 | &sin->sin_addr.s_addr, ntohs(sin->sin_port)); | |
d35eef1b EP |
1011 | } else if (st->socket->ops->family == AF_INET6) { |
1012 | struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&ctl->addr; | |
2d7cf8ef EP |
1013 | printk(KERN_INFO "%s: (re)connected to peer %pi6:%d", __func__, |
1014 | &sin->sin6_addr, ntohs(sin->sin6_port)); | |
d35eef1b EP |
1015 | } |
1016 | ||
1017 | return 0; | |
1018 | ||
1019 | err_out_release: | |
1020 | sock_release(st->socket); | |
1021 | err_out_exit: | |
1022 | st->socket = NULL; | |
1023 | return err; | |
1024 | } | |
1025 | ||
1026 | void netfs_state_exit(struct netfs_state *st) | |
1027 | { | |
1028 | if (st->socket) { | |
1029 | netfs_poll_exit(st); | |
1030 | st->socket->ops->shutdown(st->socket, 2); | |
1031 | ||
1032 | if (st->socket->ops->family == AF_INET) { | |
1033 | struct sockaddr_in *sin = (struct sockaddr_in *)&st->ctl.addr; | |
2d7cf8ef EP |
1034 | printk(KERN_INFO "%s: disconnected from peer %pi4:%d.\n", __func__, |
1035 | &sin->sin_addr.s_addr, ntohs(sin->sin_port)); | |
d35eef1b EP |
1036 | } else if (st->socket->ops->family == AF_INET6) { |
1037 | struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&st->ctl.addr; | |
2d7cf8ef EP |
1038 | printk(KERN_INFO "%s: disconnected from peer %pi6:%d", __func__, |
1039 | &sin->sin6_addr, ntohs(sin->sin6_port)); | |
d35eef1b EP |
1040 | } |
1041 | ||
1042 | sock_release(st->socket); | |
1043 | st->socket = NULL; | |
1044 | st->read_socket = NULL; | |
1045 | st->need_reset = 0; | |
1046 | } | |
1047 | } | |
1048 | ||
1049 | int pohmelfs_state_init_one(struct pohmelfs_sb *psb, struct pohmelfs_config *conf) | |
1050 | { | |
1051 | struct netfs_state *st = &conf->state; | |
1052 | int err = -ENOMEM; | |
1053 | ||
1054 | mutex_init(&st->__state_lock); | |
1055 | mutex_init(&st->__state_send_lock); | |
1056 | init_waitqueue_head(&st->thread_wait); | |
1057 | ||
1058 | st->psb = psb; | |
1059 | st->trans_root = RB_ROOT; | |
1060 | mutex_init(&st->trans_lock); | |
1061 | ||
1062 | st->size = psb->trans_data_size; | |
1063 | st->data = kmalloc(st->size, GFP_KERNEL); | |
1064 | if (!st->data) | |
1065 | goto err_out_exit; | |
1066 | ||
1067 | if (psb->perform_crypto) { | |
1068 | err = pohmelfs_crypto_engine_init(&st->eng, psb); | |
1069 | if (err) | |
1070 | goto err_out_free_data; | |
1071 | } | |
1072 | ||
1073 | err = netfs_state_init(st); | |
1074 | if (err) | |
1075 | goto err_out_free_engine; | |
1076 | ||
1077 | st->thread = kthread_run(pohmelfs_recv, st, "pohmelfs/%u", psb->idx); | |
1078 | if (IS_ERR(st->thread)) { | |
1079 | err = PTR_ERR(st->thread); | |
1080 | goto err_out_netfs_exit; | |
1081 | } | |
1082 | ||
1083 | if (!psb->active_state) | |
1084 | psb->active_state = conf; | |
1085 | ||
1086 | dprintk("%s: conf: %p, st: %p, socket: %p.\n", | |
1087 | __func__, conf, st, st->socket); | |
1088 | return 0; | |
1089 | ||
1090 | err_out_netfs_exit: | |
1091 | netfs_state_exit(st); | |
1092 | err_out_free_engine: | |
1093 | pohmelfs_crypto_engine_exit(&st->eng); | |
1094 | err_out_free_data: | |
1095 | kfree(st->data); | |
1096 | err_out_exit: | |
1097 | return err; | |
1098 | ||
1099 | } | |
1100 | ||
1101 | void pohmelfs_state_flush_transactions(struct netfs_state *st) | |
1102 | { | |
1103 | struct rb_node *rb_node; | |
1104 | struct netfs_trans_dst *dst; | |
1105 | ||
1106 | mutex_lock(&st->trans_lock); | |
1107 | for (rb_node = rb_first(&st->trans_root); rb_node; ) { | |
1108 | dst = rb_entry(rb_node, struct netfs_trans_dst, state_entry); | |
1109 | rb_node = rb_next(rb_node); | |
1110 | ||
1111 | dst->trans->result = -EINVAL; | |
1112 | netfs_trans_remove_nolock(dst, st); | |
1113 | netfs_trans_drop_dst_nostate(dst); | |
1114 | } | |
1115 | mutex_unlock(&st->trans_lock); | |
1116 | } | |
1117 | ||
1118 | static void pohmelfs_state_exit_one(struct pohmelfs_config *c) | |
1119 | { | |
1120 | struct netfs_state *st = &c->state; | |
1121 | ||
1122 | dprintk("%s: exiting, st: %p.\n", __func__, st); | |
1123 | if (st->thread) { | |
1124 | kthread_stop(st->thread); | |
1125 | st->thread = NULL; | |
1126 | } | |
1127 | ||
1128 | netfs_state_lock_send(st); | |
1129 | netfs_state_exit(st); | |
1130 | netfs_state_unlock_send(st); | |
1131 | ||
1132 | pohmelfs_state_flush_transactions(st); | |
1133 | ||
1134 | pohmelfs_crypto_engine_exit(&st->eng); | |
1135 | kfree(st->data); | |
1136 | ||
1137 | kfree(c); | |
1138 | } | |
1139 | ||
1140 | /* | |
1141 | * Initialize network stack. It searches for given ID in global | |
1142 | * configuration table, this contains information of the remote server | |
1143 | * (address (any supported by socket interface) and port, protocol and so on). | |
1144 | */ | |
1145 | int pohmelfs_state_init(struct pohmelfs_sb *psb) | |
1146 | { | |
1147 | int err = -ENOMEM; | |
1148 | ||
1149 | err = pohmelfs_copy_config(psb); | |
1150 | if (err) { | |
1151 | pohmelfs_state_exit(psb); | |
1152 | return err; | |
1153 | } | |
1154 | ||
1155 | return 0; | |
1156 | } | |
1157 | ||
1158 | void pohmelfs_state_exit(struct pohmelfs_sb *psb) | |
1159 | { | |
1160 | struct pohmelfs_config *c, *tmp; | |
1161 | ||
1162 | list_for_each_entry_safe(c, tmp, &psb->state_list, config_entry) { | |
1163 | list_del(&c->config_entry); | |
1164 | pohmelfs_state_exit_one(c); | |
1165 | } | |
1166 | } | |
1167 | ||
1168 | void pohmelfs_switch_active(struct pohmelfs_sb *psb) | |
1169 | { | |
1170 | struct pohmelfs_config *c = psb->active_state; | |
1171 | ||
1172 | if (!list_empty(&psb->state_list)) { | |
1173 | if (c->config_entry.next != &psb->state_list) { | |
1174 | psb->active_state = list_entry(c->config_entry.next, | |
1175 | struct pohmelfs_config, config_entry); | |
1176 | } else { | |
1177 | psb->active_state = list_entry(psb->state_list.next, | |
1178 | struct pohmelfs_config, config_entry); | |
1179 | } | |
1180 | ||
1181 | dprintk("%s: empty: %d, active %p -> %p.\n", | |
1182 | __func__, list_empty(&psb->state_list), c, | |
1183 | psb->active_state); | |
1184 | } else | |
1185 | psb->active_state = NULL; | |
1186 | } | |
1187 | ||
1188 | void pohmelfs_check_states(struct pohmelfs_sb *psb) | |
1189 | { | |
1190 | struct pohmelfs_config *c, *tmp; | |
1191 | LIST_HEAD(delete_list); | |
1192 | ||
1193 | mutex_lock(&psb->state_lock); | |
1194 | list_for_each_entry_safe(c, tmp, &psb->state_list, config_entry) { | |
1195 | if (pohmelfs_config_check(c, psb->idx)) { | |
1196 | ||
1197 | if (psb->active_state == c) | |
1198 | pohmelfs_switch_active(psb); | |
1199 | list_move(&c->config_entry, &delete_list); | |
1200 | } | |
1201 | } | |
1202 | pohmelfs_copy_config(psb); | |
1203 | mutex_unlock(&psb->state_lock); | |
1204 | ||
1205 | list_for_each_entry_safe(c, tmp, &delete_list, config_entry) { | |
1206 | list_del(&c->config_entry); | |
1207 | pohmelfs_state_exit_one(c); | |
1208 | } | |
1209 | } |