linux/fs/smbfs/cache.c
<<
>>
Prefs
   1/*
   2 *  cache.c
   3 *
   4 * Copyright (C) 1997 by Bill Hawes
   5 *
   6 * Routines to support directory cacheing using the page cache.
   7 * This cache code is almost directly taken from ncpfs.
   8 *
   9 * Please add a note about your changes to smbfs in the ChangeLog file.
  10 */
  11
  12#include <linux/time.h>
  13#include <linux/errno.h>
  14#include <linux/kernel.h>
  15#include <linux/mm.h>
  16#include <linux/smb_fs.h>
  17#include <linux/pagemap.h>
  18#include <linux/net.h>
  19
  20#include <asm/page.h>
  21
  22#include "smb_debug.h"
  23#include "proto.h"
  24
  25/*
  26 * Force the next attempt to use the cache to be a timeout.
  27 * If we can't find the page that's fine, it will cause a refresh.
  28 */
  29void
  30smb_invalid_dir_cache(struct inode * dir)
  31{
  32        struct smb_sb_info *server = server_from_inode(dir);
  33        union  smb_dir_cache *cache = NULL;
  34        struct page *page = NULL;
  35
  36        page = grab_cache_page(&dir->i_data, 0);
  37        if (!page)
  38                goto out;
  39
  40        if (!PageUptodate(page))
  41                goto out_unlock;
  42
  43        cache = kmap(page);
  44        cache->head.time = jiffies - SMB_MAX_AGE(server);
  45
  46        kunmap(page);
  47        SetPageUptodate(page);
  48out_unlock:
  49        unlock_page(page);
  50        page_cache_release(page);
  51out:
  52        return;
  53}
  54
  55/*
  56 * Mark all dentries for 'parent' as invalid, forcing them to be re-read
  57 */
  58void
  59smb_invalidate_dircache_entries(struct dentry *parent)
  60{
  61        struct smb_sb_info *server = server_from_dentry(parent);
  62        struct list_head *next;
  63        struct dentry *dentry;
  64
  65        spin_lock(&dcache_lock);
  66        next = parent->d_subdirs.next;
  67        while (next != &parent->d_subdirs) {
  68                dentry = list_entry(next, struct dentry, d_u.d_child);
  69                dentry->d_fsdata = NULL;
  70                smb_age_dentry(server, dentry);
  71                next = next->next;
  72        }
  73        spin_unlock(&dcache_lock);
  74}
  75
  76/*
  77 * dget, but require that fpos and parent matches what the dentry contains.
  78 * dentry is not known to be a valid pointer at entry.
  79 */
  80struct dentry *
  81smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
  82{
  83        struct dentry *dent = dentry;
  84        struct list_head *next;
  85
  86        if (d_validate(dent, parent)) {
  87                if (dent->d_name.len <= SMB_MAXNAMELEN &&
  88                    (unsigned long)dent->d_fsdata == fpos) {
  89                        if (!dent->d_inode) {
  90                                dput(dent);
  91                                dent = NULL;
  92                        }
  93                        return dent;
  94                }
  95                dput(dent);
  96        }
  97
  98        /* If a pointer is invalid, we search the dentry. */
  99        spin_lock(&dcache_lock);
 100        next = parent->d_subdirs.next;
 101        while (next != &parent->d_subdirs) {
 102                dent = list_entry(next, struct dentry, d_u.d_child);
 103                if ((unsigned long)dent->d_fsdata == fpos) {
 104                        if (dent->d_inode)
 105                                dget_locked(dent);
 106                        else
 107                                dent = NULL;
 108                        goto out_unlock;
 109                }
 110                next = next->next;
 111        }
 112        dent = NULL;
 113out_unlock:
 114        spin_unlock(&dcache_lock);
 115        return dent;
 116}
 117
 118
 119/*
 120 * Create dentry/inode for this file and add it to the dircache.
 121 */
 122int
 123smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
 124               struct smb_cache_control *ctrl, struct qstr *qname,
 125               struct smb_fattr *entry)
 126{
 127        struct dentry *newdent, *dentry = filp->f_path.dentry;
 128        struct inode *newino, *inode = dentry->d_inode;
 129        struct smb_cache_control ctl = *ctrl;
 130        int valid = 0;
 131        int hashed = 0;
 132        ino_t ino = 0;
 133
 134        qname->hash = full_name_hash(qname->name, qname->len);
 135
 136        if (dentry->d_op && dentry->d_op->d_hash)
 137                if (dentry->d_op->d_hash(dentry, qname) != 0)
 138                        goto end_advance;
 139
 140        newdent = d_lookup(dentry, qname);
 141
 142        if (!newdent) {
 143                newdent = d_alloc(dentry, qname);
 144                if (!newdent)
 145                        goto end_advance;
 146        } else {
 147                hashed = 1;
 148                memcpy((char *) newdent->d_name.name, qname->name,
 149                       newdent->d_name.len);
 150        }
 151
 152        if (!newdent->d_inode) {
 153                smb_renew_times(newdent);
 154                entry->f_ino = iunique(inode->i_sb, 2);
 155                newino = smb_iget(inode->i_sb, entry);
 156                if (newino) {
 157                        smb_new_dentry(newdent);
 158                        d_instantiate(newdent, newino);
 159                        if (!hashed)
 160                                d_rehash(newdent);
 161                }
 162        } else
 163                smb_set_inode_attr(newdent->d_inode, entry);
 164
 165        if (newdent->d_inode) {
 166                ino = newdent->d_inode->i_ino;
 167                newdent->d_fsdata = (void *) ctl.fpos;
 168                smb_new_dentry(newdent);
 169        }
 170
 171        if (ctl.idx >= SMB_DIRCACHE_SIZE) {
 172                if (ctl.page) {
 173                        kunmap(ctl.page);
 174                        SetPageUptodate(ctl.page);
 175                        unlock_page(ctl.page);
 176                        page_cache_release(ctl.page);
 177                }
 178                ctl.cache = NULL;
 179                ctl.idx  -= SMB_DIRCACHE_SIZE;
 180                ctl.ofs  += 1;
 181                ctl.page  = grab_cache_page(&inode->i_data, ctl.ofs);
 182                if (ctl.page)
 183                        ctl.cache = kmap(ctl.page);
 184        }
 185        if (ctl.cache) {
 186                ctl.cache->dentry[ctl.idx] = newdent;
 187                valid = 1;
 188        }
 189        dput(newdent);
 190
 191end_advance:
 192        if (!valid)
 193                ctl.valid = 0;
 194        if (!ctl.filled && (ctl.fpos == filp->f_pos)) {
 195                if (!ino)
 196                        ino = find_inode_number(dentry, qname);
 197                if (!ino)
 198                        ino = iunique(inode->i_sb, 2);
 199                ctl.filled = filldir(dirent, qname->name, qname->len,
 200                                     filp->f_pos, ino, DT_UNKNOWN);
 201                if (!ctl.filled)
 202                        filp->f_pos += 1;
 203        }
 204        ctl.fpos += 1;
 205        ctl.idx  += 1;
 206        *ctrl = ctl;
 207        return (ctl.valid || !ctl.filled);
 208}
 209