Whamcloud - gitweb
landing smfs.
[fs/lustre-release.git] / lustre / smfs / cache_space.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  lustre/smfs/cache_space.c
5  *  A library of functions to manage cache space based on ARC 
6  *  (modified LRU) replacement algorithm.
7  *  
8  *  Copyright (c) 2002, 2003 Cluster File Systems, Inc.
9  *
10  *   This file is part of Lustre, http://www.lustre.org.
11  *
12  *   Lustre is free software; you can redistribute it and/or
13  *   modify it under the terms of version 2 of the GNU General Public
14  *   License as published by the Free Software Foundation.
15  *
16  *   Lustre is distributed in the hope that it will be useful,
17  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *   GNU General Public License for more details.
20  *
21  *   You should have received a copy of the GNU General Public License
22  *   along with Lustre; if not, write to the Free Software
23  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  */
25 #define DEBUG_SUBSYSTEM S_SM
26
27 #include <linux/lustre_log.h>
28 #include <linux/lustre_smfs.h>
29
30 #include "smfs_internal.h"
31
32 struct cache_purge_param {
33         int nfract;     /* Percentage of cache dirty to activate cpurge */
34         int ndirty;     /* Maximum number of objects to write out per
35                            wake-cycle */
36         int interval;   /* jiffies delay between cache purge */
37         int nfract_sync;/* Percentage of cache dirty to activate 
38                            cpurge synchronously */
39         int nfract_stop_cpurge; /* Percentage of cache dirty to stop cpurge */
40 } cf_prm = {30, 512, 600 * HZ, 60, 20};
41
42 static struct cache_purge_queue smfs_cpq;
43 static struct cache_purge_queue *cpq = &smfs_cpq;
44
45 int cache_space_hook_init(struct super_block *sb)
46 {
47         struct smfs_super_info  *smfs_info = S2SMI(sb);
48
49         SMFS_SET_CACHE_HOOK(smfs_info);
50         return 0;
51 }
52 int cache_space_hook_exit(struct super_block *sb)
53 {
54         struct smfs_super_info  *smfs_info = S2SMI(sb);
55
56         SMFS_CLEAN_CACHE_HOOK(smfs_info);
57         return 0;
58 }
59 int smfs_cache_hook(struct inode *inode)
60 {
61         struct smfs_super_info  *smfs_info = I2CSB(inode);
62                                                                                                                              
63         if (SMFS_CACHE_HOOK(smfs_info) && SMFS_INIT_REC(smfs_info)
64             && SMFS_INODE_CACHE_HOOK(inode))
65                         return 1;
66         else
67                 return 0;
68 }
69
70 static int cache_leaf_node(struct dentry *dentry, __u64 *active_entry)
71 {
72         struct inode *inode = dentry->d_inode;
73
74         if (S_ISDIR(inode->i_mode)) {
75                 if (inode->i_nlink != 2)
76                         return 0;
77                 if (!strncmp(dentry->d_name.name, "lost+found", dentry->d_name.len))
78                         return 0;
79                 LASSERT(active_entry != NULL);
80                 get_active_entry(inode, active_entry);
81                 return(*active_entry > 0 ? 0 : 1);
82         } else {
83                 if (inode->i_nlink != 1)
84                         return 0;
85                 if (!strncmp(dentry->d_name.name, KML_LOG_NAME, dentry->d_name.len) ||
86                     !strncmp(dentry->d_name.name, CACHE_LRU_LOG, dentry->d_name.len))
87                         return 0;
88                 return 1;
89         }
90 }
91 static int cache_pre_leaf_node(struct dentry *dentry, __u64 *active_entry, int op)
92 {
93         if (((op == 0 && dentry->d_inode->i_nlink == 0) ||
94             (op == 1 && dentry->d_inode->i_nlink == 2)) &&
95             strncmp(dentry->d_name.name, KML_LOG_NAME, dentry->d_name.len) &&
96             strncmp(dentry->d_name.name, CACHE_LRU_LOG, dentry->d_name.len))
97                 return 1;
98         else if ((op == 2 && dentry->d_inode->i_nlink == 0) ||
99                  (op == 3 && dentry->d_inode->i_nlink == 3)) {
100                 LASSERT(active_entry != NULL);
101                 get_active_entry(dentry->d_inode, active_entry);
102                 return(*active_entry > 0 ? 0 : 1);
103         }
104         return 0;
105 }
106
107 static int set_lru_logcookie(struct inode *inode, void *handle,
108                              struct llog_cookie *logcookie)
109 {
110         struct fsfilt_operations *fsops = I2CSB(inode)->sm_fsfilt;
111         int rc;
112         rc = fsops->fs_set_xattr(inode, handle, XATTR_SMFS_CACHE_LOGCOOKIE,
113                                  logcookie, sizeof(*logcookie));
114         RETURN(rc);
115 }
116 static int get_lru_logcookie(struct inode *inode, struct llog_cookie *logcookie)
117 {
118         struct fsfilt_operations *fsops = I2CSB(inode)->sm_fsfilt;
119         int rc;
120         rc = fsops->fs_get_xattr(inode, XATTR_SMFS_CACHE_LOGCOOKIE,
121                                  logcookie, sizeof(*logcookie));
122         RETURN(rc);
123 }
124
125 static int try2purge_from_cache(struct ll_fid cfid, struct ll_fid pfid)
126 {
127         struct inode *inode, *parent;
128         struct super_block *sb = cpq->cpq_sb;
129         //struct llog_cookie logcookie;
130         __u32 hoard_priority = 0;
131         int rc = 0;
132         ENTRY;
133
134         inode = iget(sb, cfid.id);
135         if (IS_ERR(inode)) {
136                 CERROR("not existent inode: "LPX64"/%u\n", 
137                        cfid.id, cfid.generation);
138                 RETURN(-ENOENT);
139         }
140         parent = iget(sb, pfid.id);
141         if (IS_ERR(parent)) {
142                 CERROR("not existent inode: "LPX64"/%u\n", 
143                        pfid.id, pfid.generation);
144                 iput(inode);
145                 RETURN(-ENOENT);
146         }
147
148         CWARN("inode/parent %lu:%lu on the lru list\n",
149               inode->i_ino, parent->i_ino);
150
151         rc = get_hoard_priority(inode, &hoard_priority);
152         if (hoard_priority) {
153                 CWARN("inode %lu set hoard\n", inode->i_ino);
154                 GOTO(out, rc);
155         }
156         if (atomic_read(&inode->i_count) > 1 || (inode->i_state & I_DIRTY)) {
157                 CWARN("inode %lu is busy\n", inode->i_ino);
158                 GOTO(out, rc = 0);
159         }
160
161 out:
162         iput(inode);
163         iput(parent);
164         RETURN(rc);
165 }
166
167 static int cache_lru_get_rec_cb(struct llog_handle *llh,
168                                 struct llog_rec_hdr *rec, void *data)
169 {
170         struct llog_lru_rec *llr;
171         int count = *(int *)data, rc = 0;
172         ENTRY;
173
174         if (!(le32_to_cpu(llh->lgh_hdr->llh_flags) & LLOG_F_IS_PLAIN)) {
175                 CERROR("log is not plain\n");
176                 RETURN(-EINVAL);
177         }
178         if (rec->lrh_type != CACHE_LRU_REC) {
179                 CERROR("log record type error\n");
180                 RETURN(-EINVAL);
181         }
182
183         llr = (struct llog_lru_rec *)rec;
184
185         if (try2purge_from_cache(llr->llr_cfid, llr->llr_pfid)==1){
186                 CDEBUG(D_INODE, "purge ino/gen "LPX64"/%u from cache\n",
187                        llr->llr_cfid.id, llr->llr_cfid.generation);
188                 count --;
189                 if (count == 0)
190                         rc = LLOG_PROC_BREAK;
191                 *(int *)data = count;
192         }
193
194         RETURN(rc);
195 }
196
197 static int cpurge_stop(void)
198 {
199         struct fsfilt_operations *fsops = S2SMI(cpq->cpq_sb)->sm_fsfilt;
200         struct obd_statfs osfs;
201         int rc, free;
202
203         rc = fsops->fs_statfs(cpq->cpq_sb, &osfs);
204         LASSERT(rc == 0);
205
206         free = osfs.os_bfree * 100;
207         if (free < cf_prm.nfract_stop_cpurge * osfs.os_blocks)
208                 return 1;
209         return 0;
210 }
211
212 static int cache_balance_state(void)
213 {
214         struct fsfilt_operations *fsops = S2SMI(cpq->cpq_sb)->sm_fsfilt;
215         struct obd_statfs osfs;
216         int rc, free;
217
218         rc = fsops->fs_statfs(cpq->cpq_sb, &osfs);
219         LASSERT(rc == 0);
220
221         free = (osfs.os_blocks - osfs.os_bfree) * 100; 
222         if (free > cf_prm.nfract * osfs.os_blocks) {
223                 if (free < cf_prm.nfract_sync)
224                         return 1;
225                 return 0;
226         }
227         return -1; 
228 }
229
230 void wakeup_cpurge(void)
231 {
232         wake_up(&cpq->cpq_waitq);
233 }
234
235 /* walk the lru llog to purge count number of objects */
236 static int purge_some_cache(int *count)
237 {
238         int rc;
239         ENTRY;
240
241         rc = llog_cat_process(cpq->cpq_loghandle,
242                               (llog_cb_t)cache_lru_get_rec_cb,
243                               count);
244         if (!rc)
245                 CDEBUG(D_INODE, "no enough objects available\n");
246
247         RETURN(rc);
248 }
249
250 #define CFLUSH_NR 512
251 static void check_cache_space(void)
252 {
253         int state = cache_balance_state();
254         ENTRY;
255
256         if (state < 0)
257                 return;
258
259         wakeup_cpurge();
260
261         if (state > 0) {
262                 int count = CFLUSH_NR;
263                 purge_some_cache(&count);
264         }
265 }
266
267 void cache_space_pre(struct inode *inode, int op)
268 {
269         ENTRY;
270
271         /* FIXME have not used op */
272         check_cache_space();
273 }
274
275 static int cache_space_hook_lru(struct inode *inode, struct inode *parent, 
276                      void *handle, int op, int flags)
277 {
278         struct fsfilt_operations *fsops = S2SMI(cpq->cpq_sb)->sm_fsfilt;
279         struct llog_ctxt *ctxt = cpq->cpq_loghandle->lgh_ctxt;
280         struct llog_lru_rec *llr = NULL; 
281         struct llog_cookie *logcookie = NULL;
282         int cookie_size = sizeof(struct llog_cookie);
283         int rc = 0, err;
284         ENTRY;
285
286         LASSERT(ctxt != NULL);
287
288         if (op & ~(CACHE_SPACE_DELETE | CACHE_SPACE_INSERT |CACHE_SPACE_COMMIT))
289                 RETURN(-EINVAL);
290
291         OBD_ALLOC(logcookie, cookie_size);
292         if (!logcookie)
293                 GOTO(out, rc = -ENOMEM);
294
295         if (op & CACHE_SPACE_DELETE) {
296                 rc = get_lru_logcookie(inode, logcookie);
297                 if (rc < 0) 
298                         GOTO(out, rc);
299
300                 if (logcookie->lgc_lgl.lgl_oid == 0) {
301                         CWARN("inode %lu/%u is not in lru list\n",
302                               inode->i_ino, inode->i_generation);
303                         GOTO(insert, rc = -ENOENT); 
304                 }
305                 if (flags && llog_cat_half_bottom(logcookie, ctxt->loc_handle))
306                         GOTO(out, rc = 0);
307
308                 rc = llog_cancel(ctxt, 1, logcookie, 0, NULL);
309                 if (!rc) {
310                         memset(logcookie, 0, cookie_size);
311                         rc = set_lru_logcookie(inode, handle, logcookie);
312                         if (rc)
313                                 GOTO(out, rc);
314                 } else {
315                         CERROR("failed at llog_cancel: %d\n", rc);
316                         GOTO(out, rc);
317                 }
318         }
319
320 insert:
321         if (op & CACHE_SPACE_INSERT) {
322                 LASSERT(parent != NULL);
323                 OBD_ALLOC(llr, sizeof(*llr));
324                 if (llr == NULL)
325                         GOTO(out, rc = -ENOMEM); 
326
327                 llr->llr_hdr.lrh_len = llr->llr_tail.lrt_len = sizeof(*llr);
328                 llr->llr_hdr.lrh_type = CACHE_LRU_REC;
329                 llr->llr_cfid.id = inode->i_ino;
330                 llr->llr_cfid.generation = inode->i_generation;
331                 llr->llr_cfid.f_type = inode->i_mode & S_IFMT;
332                 llr->llr_pfid.id = parent->i_ino;
333                 llr->llr_pfid.generation = parent->i_generation;
334                 llr->llr_pfid.f_type = parent->i_mode & S_IFMT;
335
336                 rc = llog_add(ctxt, &llr->llr_hdr, NULL, logcookie, 1, NULL);
337                 if (rc != 1) {
338                         CERROR("failed at llog_add: %d\n", rc);
339                         GOTO(out, rc);
340                 }
341                 rc = set_lru_logcookie(inode, handle, logcookie);
342         }
343
344         if (op & CACHE_SPACE_COMMIT) {
345                 if (handle) {
346                         err = fsops->fs_commit(inode, handle, 0);
347                         if (err) {
348                                 CERROR("error committing transaction: %d\n", err);
349                                 if (!rc)
350                                         rc = err;
351                         }
352                 }
353         }
354 out: 
355         if (logcookie)
356                 OBD_FREE(logcookie, cookie_size);
357         if (llr)
358                 OBD_FREE(llr, sizeof(*llr));
359         RETURN(rc);
360 }
361
362 static int cache_purge_thread(void *args)
363 {
364         unsigned long flags;
365         struct l_wait_info lwi = LWI_TIMEOUT(cf_prm.interval * HZ, NULL, NULL);
366         ENTRY;
367
368         lock_kernel();
369         kportal_daemonize("wb_cache_purge");
370
371         SIGNAL_MASK_LOCK(current, flags);
372         sigfillset(&current->blocked);
373         RECALC_SIGPENDING;
374         SIGNAL_MASK_UNLOCK(current, flags);
375
376         unlock_kernel();
377         complete(&cpq->cpq_comp);
378
379         while (1) {
380                 int ndirty = cf_prm.ndirty;
381
382                 purge_some_cache(&ndirty);
383                 if (ndirty > 0 || cpurge_stop())
384                         l_wait_event(cpq->cpq_waitq, 
385                                      cpq->cpq_flags & SVC_STOPPING,
386                                      &lwi);
387                 if (cpq->cpq_flags & SVC_STOPPING) {
388                         cpq->cpq_flags &= ~SVC_STOPPING; 
389                         EXIT;
390                         break;
391                 }
392         }
393         cpq->cpq_flags = SVC_STOPPED;
394         complete(&cpq->cpq_comp);
395         return 0;
396 }
397
398 int cache_space_hook_setup(struct super_block *sb)
399 {
400         struct llog_ctxt *ctxt;
401         int rc;
402         ENTRY;
403
404         /* first to initialize the cache lru catalog on local fs */
405         rc = llog_catalog_setup(&ctxt, CACHE_LRU_LOG,
406                                 S2SMI(sb)->smsi_ctxt,
407                                 S2SMI(sb)->sm_fsfilt,
408                                 S2SMI(sb)->smsi_logs_dir,
409                                 S2SMI(sb)->smsi_objects_dir);
410         if (rc) {
411                 CERROR("failed to initialize cache lru list catalog %d\n", rc);
412                 RETURN(rc);
413         }
414         cpq->cpq_sb = sb;
415         cpq->cpq_loghandle = ctxt->loc_handle; 
416
417         /* start cache purge daemon, only one daemon now */
418         init_waitqueue_head(&cpq->cpq_waitq);
419         init_completion(&cpq->cpq_comp);
420         cpq->cpq_flags = 0;
421
422         rc = kernel_thread(cache_purge_thread, NULL, CLONE_VM | CLONE_FILES);
423         if (rc < 0) {
424                 CERROR("cannot start thread: %d\n", rc);
425                 GOTO(err_out, rc);
426         }
427         wait_for_completion(&cpq->cpq_comp);
428
429         RETURN(0);
430 err_out:
431         llog_catalog_cleanup(ctxt);
432         RETURN(rc);
433 }
434
435 int cache_space_hook_cleanup(void)
436 {
437         int rc;
438         ENTRY;
439
440         init_completion(&cpq->cpq_comp);
441         cpq->cpq_flags = SVC_STOPPING;
442         wake_up(&cpq->cpq_waitq);
443         wait_for_completion(&cpq->cpq_comp);
444
445         rc = llog_catalog_cleanup(cpq->cpq_loghandle->lgh_ctxt);
446         if (rc) 
447                 CERROR("failed to clean up cache lru list catalog %d\n", rc);
448
449         RETURN(rc);
450 }
451
452 static int cache_space_hook_create(void *handle, struct inode *dir,
453                                    struct dentry *dentry, struct inode *new_dir,
454                                    struct dentry *new_dentry)
455 {
456         __u64 active_entry = 0;
457         int rc;
458
459         LASSERT(cache_leaf_node(dentry, NULL));
460         rc = cache_space_hook_lru(dentry->d_inode, dir, handle,
461                                   CACHE_SPACE_INSERT, 0);
462         if (rc)
463                 RETURN(rc);
464         if (cache_leaf_node(dentry->d_parent, &active_entry)) {
465                 rc = cache_space_hook_lru(dir,NULL,handle,CACHE_SPACE_DELETE,0);
466                 if (rc)
467                         RETURN(rc);
468         }
469         if (!active_entry)
470                 rc = get_active_entry(dir, &active_entry);
471         active_entry ++;
472         if (!rc)
473                 rc = set_active_entry(dir, &active_entry, handle); 
474         RETURN(rc);
475 }
476 static int cache_space_hook_lookup(void *handle, struct inode *dir,
477                                    struct dentry *dentry, struct inode *new_dir,
478                                    struct dentry *new_dentry)
479 {
480         __u64 active_entry;
481         int rc = 0;
482
483         if (cache_leaf_node(dentry, &active_entry))        
484                 rc = cache_space_hook_lru(dentry->d_inode, dir, handle,
485                                 CACHE_SPACE_DELETE | CACHE_SPACE_INSERT,1);
486         RETURN(rc);
487 }
488 static int cache_space_hook_link(void *handle, struct inode *dir,
489                                  struct dentry *dentry, struct inode *new_dir,
490                                  struct dentry *new_dentry)
491 {
492         __u64 active_entry = 0;
493         int rc = 0;
494
495         if (cache_pre_leaf_node(dentry, NULL, 1)) {
496                 rc = cache_space_hook_lru(dentry->d_inode, NULL,
497                                           handle, CACHE_SPACE_DELETE, 0); 
498                 if (rc)
499                         RETURN(rc);
500         }
501
502         if (cache_leaf_node(dentry->d_parent, &active_entry)) {
503                 rc = cache_space_hook_lru(dir,NULL,handle,CACHE_SPACE_DELETE,0);
504                 if (rc)
505                         RETURN(rc);
506         }
507
508         if (!active_entry)
509                 rc = get_active_entry(dir, &active_entry);
510         active_entry ++;
511         if (!rc)
512                 rc = set_active_entry(dir, &active_entry, handle); 
513         RETURN(rc);
514 }
515 static int cache_space_hook_unlink(void *handle, struct inode *dir,
516                                    struct dentry *dentry, struct inode *new_dir,
517                                    struct dentry *new_dentry)
518 {
519         __u64 active_entry;
520         int rc = 0;
521
522         if (cache_pre_leaf_node(dentry, NULL, 0))
523                 rc = cache_space_hook_lru(dentry->d_inode, NULL, 
524                                           handle, CACHE_SPACE_DELETE, 0);
525         else if (cache_leaf_node(dentry, NULL))
526                         rc = cache_space_hook_lru(dentry->d_inode, dir,
527                                                   handle, CACHE_SPACE_INSERT,0);
528         if (rc)
529                 RETURN(rc);
530
531         rc = get_active_entry(dir, &active_entry);
532         active_entry --;
533         if (!rc)
534                 rc = set_active_entry(dir, &active_entry, handle);
535         if (!rc && cache_leaf_node(dentry->d_parent, &active_entry)) 
536                 rc = cache_space_hook_lru(dir,
537                                           dentry->d_parent->d_parent->d_inode,
538                                           handle, CACHE_SPACE_INSERT, 0);
539         RETURN(rc);
540 }
541 static int cache_space_hook_mkdir(void *handle, struct inode *dir, 
542                                   struct dentry *dentry, struct inode *new_dir,
543                                   struct dentry *new_dentry)
544 {
545         __u64 active_entry;
546         int rc;
547
548         LASSERT(cache_leaf_node(dentry, &active_entry));
549         rc = cache_space_hook_lru(dentry->d_inode, dir, handle,
550                                   CACHE_SPACE_INSERT,0);
551
552         if (!rc && cache_pre_leaf_node(dentry->d_parent, &active_entry, 3))
553                 rc = cache_space_hook_lru(dir,NULL,handle,CACHE_SPACE_DELETE,0);
554         RETURN(rc);
555 }
556 static int cache_space_hook_rmdir(void *handle, struct inode *dir, 
557                                   struct dentry *dentry, struct inode *new_dir,
558                                   struct dentry *new_dentry)
559 {
560         __u64 active_entry;
561         int rc;
562
563         LASSERT(cache_pre_leaf_node(dentry, &active_entry, 2));
564         rc = cache_space_hook_lru(dentry->d_inode, NULL, handle,
565                                   CACHE_SPACE_DELETE, 0);
566
567         if (!rc && cache_leaf_node(dentry->d_parent, &active_entry))
568                 rc = cache_space_hook_lru(dir,
569                                           dentry->d_parent->d_parent->d_inode,
570                                           handle, CACHE_SPACE_INSERT, 0);
571         RETURN(rc);
572 }
573 static int cache_space_hook_rename(void *handle, struct inode *old_dir, 
574                         struct dentry *old_dentry, struct inode *new_dir,
575                         struct dentry *new_dentry)
576 {
577         __u64 active_entry;
578         int rc = 0;
579
580         if (new_dentry->d_inode) {
581                 if (cache_pre_leaf_node(new_dentry, NULL, 0))
582                         rc = cache_space_hook_lru(new_dentry->d_inode, NULL,
583                                                   handle, CACHE_SPACE_DELETE,0);
584                 else if (cache_leaf_node(new_dentry, NULL))
585                         rc = cache_space_hook_lru(new_dentry->d_inode,
586                                                   new_dir, handle,
587                                                   CACHE_SPACE_INSERT,0);
588         }
589
590         if (rc || old_dir == new_dir)
591                 RETURN(rc);
592
593         if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
594                 if (cache_leaf_node(new_dentry->d_parent, &active_entry)) {
595                         rc = cache_space_hook_lru(new_dir, NULL, handle,
596                                                   CACHE_SPACE_DELETE, 0);
597                         if (rc)
598                                 RETURN(rc);
599                 }
600                 if (!active_entry)
601                         rc = get_active_entry(new_dir, &active_entry);
602                 active_entry ++;
603                 if (!rc)
604                         rc = set_active_entry(new_dir, &active_entry, handle);
605                 if (rc)
606                         RETURN(rc);
607                 rc = get_active_entry(old_dir, &active_entry);
608                 active_entry --;
609                 if (!rc)
610                         rc = set_active_entry(old_dir, &active_entry, handle);
611         } else if (cache_pre_leaf_node(new_dentry->d_parent, &active_entry, 3))
612                 rc = cache_space_hook_lru(new_dir, NULL, handle,
613                                           CACHE_SPACE_DELETE, 0);
614
615         if (!rc && cache_leaf_node(old_dentry->d_parent, &active_entry))
616                 rc = cache_space_hook_lru(old_dir,
617                                         old_dentry->d_parent->d_parent->d_inode,
618                                         handle, CACHE_SPACE_INSERT, 0);
619         RETURN(rc);
620 }
621
622 typedef int (*cache_hook_op)(void *handle, struct inode *old_dir,
623                              struct dentry *old_dentry, struct inode *new_dir,
624                              struct dentry *new_dentry);
625 static  cache_hook_op cache_space_hook_ops[CACHE_HOOK_MAX + 1] = {
626         [CACHE_HOOK_CREATE]     cache_space_hook_create,
627         [CACHE_HOOK_LOOKUP]     cache_space_hook_lookup,
628         [CACHE_HOOK_LINK]       cache_space_hook_link,
629         [CACHE_HOOK_UNLINK]     cache_space_hook_unlink,
630         [CACHE_HOOK_SYMLINK]    cache_space_hook_create,
631         [CACHE_HOOK_MKDIR]      cache_space_hook_mkdir,
632         [CACHE_HOOK_RMDIR]      cache_space_hook_rmdir,
633         [CACHE_HOOK_MKNOD]      cache_space_hook_create,
634         [CACHE_HOOK_RENAME]     cache_space_hook_rename,
635 };
636
637 int cache_space_post(int op, void *handle, struct inode *old_dir,
638                struct dentry *old_dentry, struct inode *new_dir,
639                struct dentry *new_dentry)
640 {
641         int rc;
642         ENTRY;
643
644         LASSERT(op <= CACHE_HOOK_MAX && cache_space_hook_ops[op] != NULL);
645
646         rc = cache_space_hook_ops[op](handle, old_dir, old_dentry,
647                                       new_dir, new_dentry);
648         RETURN(rc);
649 }