[JFFS2] Add symlink caching support.
authorArtem B. Bityuckiy <dedekind@infradead.org>
Tue, 1 Mar 2005 10:50:52 +0000 (10:50 +0000)
committerThomas Gleixner <tglx@mtd.linutronix.de>
Mon, 23 May 2005 10:48:15 +0000 (12:48 +0200)
Signed-off-by: Artem B. Bityuckiy <dedekind@infradead.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
fs/jffs2/dir.c
fs/jffs2/read.c
fs/jffs2/readinode.c
fs/jffs2/symlink.c
fs/jffs2/write.c

index 757306fa3ff477aebcfa0ab861d45bfd7360acdd..6421be874ce3db128e59a18c96fa9595b6edeb34 100644 (file)
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: dir.c,v 1.84 2004/11/16 20:36:11 dwmw2 Exp $
+ * $Id: dir.c,v 1.85 2005/03/01 10:34:03 dedekind Exp $
  *
  */
 
@@ -296,11 +296,11 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
        struct jffs2_full_dirent *fd;
        int namelen;
        uint32_t alloclen, phys_ofs;
-       int ret;
+       int ret, targetlen = strlen(target);
 
        /* FIXME: If you care. We'd need to use frags for the target
           if it grows much more than this */
-       if (strlen(target) > 254)
+       if (targetlen > 254)
                return -EINVAL;
 
        ri = jffs2_alloc_raw_inode();
@@ -314,7 +314,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
         * Just the node will do for now, though 
         */
        namelen = dentry->d_name.len;
-       ret = jffs2_reserve_space(c, sizeof(*ri) + strlen(target), &phys_ofs, &alloclen, ALLOC_NORMAL);
+       ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
 
        if (ret) {
                jffs2_free_raw_inode(ri);
@@ -333,16 +333,16 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
 
        f = JFFS2_INODE_INFO(inode);
 
-       inode->i_size = strlen(target);
+       inode->i_size = targetlen;
        ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size);
        ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size);
        ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
 
        ri->compr = JFFS2_COMPR_NONE;
-       ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target)));
+       ri->data_crc = cpu_to_je32(crc32(0, target, targetlen));
        ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
        
-       fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL);
+       fn = jffs2_write_dnode(c, f, ri, target, targetlen, phys_ofs, ALLOC_NORMAL);
 
        jffs2_free_raw_inode(ri);
 
@@ -353,6 +353,20 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
                jffs2_clear_inode(inode);
                return PTR_ERR(fn);
        }
+
+       /* We use f->dents field to store the target path. */
+       f->dents = kmalloc(targetlen + 1, GFP_KERNEL);
+       if (!f->dents) {
+               printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
+               up(&f->sem);
+               jffs2_complete_reservation(c);
+               jffs2_clear_inode(inode);
+               return -ENOMEM;
+       }
+
+       memcpy(f->dents, target, targetlen + 1);
+       D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->dents));
+
        /* No data here. Only a metadata node, which will be 
           obsoleted by the first data write
        */
index eb493dc06db74ce553832a4fa598566e69214c2a..c7f9068907cfa6a6f4b13e2694b788ed389cdd11 100644 (file)
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: read.c,v 1.38 2004/11/16 20:36:12 dwmw2 Exp $
+ * $Id: read.c,v 1.39 2005/03/01 10:34:03 dedekind Exp $
  *
  */
 
@@ -214,33 +214,3 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
        return 0;
 }
 
-/* Core function to read symlink target. */
-char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
-{
-       char *buf;
-       int ret;
-
-       down(&f->sem);
-
-       if (!f->metadata) {
-               printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino);
-               up(&f->sem);
-               return ERR_PTR(-EINVAL);
-       }
-       buf = kmalloc(f->metadata->size+1, GFP_USER);
-       if (!buf) {
-               up(&f->sem);
-               return ERR_PTR(-ENOMEM);
-       }
-       buf[f->metadata->size]=0;
-
-       ret = jffs2_read_dnode(c, f, f->metadata, buf, 0, f->metadata->size);
-
-       up(&f->sem);
-
-       if (ret) {
-               kfree(buf);
-               return ERR_PTR(ret);
-       }
-       return buf;
-}
index a1980a9da531a7d893ffbf6437da9704c3af8ba4..ef552477c813f4d5a126f64c25a7fd3c320dca0b 100644 (file)
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: readinode.c,v 1.118 2005/02/27 23:01:33 dwmw2 Exp $
+ * $Id: readinode.c,v 1.119 2005/03/01 10:34:03 dedekind Exp $
  *
  */
 
@@ -623,6 +623,40 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
                   case. */
                if (!je32_to_cpu(latest_node->isize))
                        latest_node->isize = latest_node->dsize;
+
+               if (f->inocache->state != INO_STATE_CHECKING) {
+                       /* Symlink's inode data is the target path. Read it and
+                        * keep in RAM to facilitate quick follow symlink operation.
+                        * We use f->dents field to store the target path, which
+                        * is somewhat ugly. */
+                       f->dents = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL);
+                       if (!f->dents) {
+                               printk(KERN_WARNING "Can't allocate %d bytes of memory "
+                                               "for the symlink target path cache\n",
+                                               je32_to_cpu(latest_node->csize));
+                               up(&f->sem);
+                               jffs2_do_clear_inode(c, f);
+                               return -ENOMEM;
+                       }
+                       
+                       ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node),
+                                               je32_to_cpu(latest_node->csize), &retlen, (char *)f->dents);
+                       
+                       if (ret  || retlen != je32_to_cpu(latest_node->csize)) {
+                               if (retlen != je32_to_cpu(latest_node->csize))
+                                       ret = -EIO;
+                               kfree(f->dents);
+                               f->dents = NULL;
+                               up(&f->sem);
+                               jffs2_do_clear_inode(c, f);
+                               return -ret;
+                       }
+
+                       ((char *)f->dents)[je32_to_cpu(latest_node->csize)] = '\0';
+                       D1(printk(KERN_DEBUG "jffs2_do_read_inode(): symlink's target '%s' cached\n",
+                                               (char *)f->dents));
+               }
+               
                /* fall through... */
 
        case S_IFBLK:
@@ -683,12 +717,20 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
 
        jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
 
-       fds = f->dents;
+       /* For symlink inodes we us f->dents to store the target path name */
+       if (S_ISLNK(OFNI_EDONI_2SFFJ(f)->i_mode)) {
+               if (f->dents) {
+                       kfree(f->dents);
+                       f->dents = NULL;
+               }
+       } else {
+               fds = f->dents;
 
-       while(fds) {
-               fd = fds;
-               fds = fd->next;
-               jffs2_free_full_dirent(fd);
+               while(fds) {
+                       fd = fds;
+                       fds = fd->next;
+                       jffs2_free_full_dirent(fd);
+               }
        }
 
        if (f->inocache && f->inocache->state != INO_STATE_CHECKING) {
index 7b1820d13712040e0d1aadda20e4b8459e7f5e0a..65ab6b001dcac9d24d9d40aa8bc8527980a0587b 100644 (file)
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: symlink.c,v 1.14 2004/11/16 20:36:12 dwmw2 Exp $
+ * $Id: symlink.c,v 1.16 2005/03/01 10:50:48 dedekind Exp $
  *
  */
 
 #include "nodelist.h"
 
 static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
-static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd);
 
 struct inode_operations jffs2_symlink_inode_operations =
 {      
        .readlink =     generic_readlink,
        .follow_link =  jffs2_follow_link,
-       .put_link =     jffs2_put_link,
        .setattr =      jffs2_setattr
 };
 
 static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
-       unsigned char *buf;
-       buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode));
-       nd_set_link(nd, buf);
+       struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+       
+       /*
+        * We don't acquire the f->sem mutex here since the only data we
+        * use is f->dents which in case of the symlink inode points to the
+        * symlink's target path.
+        *
+        * 1. If we are here the inode has already built and f->dents has
+        * to point to the target path.
+        * 2. Nobody uses f->dents (if the inode is symlink's inode). The
+        * exception is inode freeing function which frees f->dents. But
+        * it can't be called while we are here and before VFS has
+        * stopped using our f->dents string which we provide by means of
+        * nd_set_link() call.
+        */
+       
+       if (!f->dents) {
+               printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
+               return -EIO;
+       }
+       D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->dents));
+
+       nd_set_link(nd, (char *)f->dents);
+       
+       /*
+        * We unlock the f->sem mutex but VFS will use the f->dents string. This is safe
+        * since the only way that may cause f->dents to be changed is iput() operation.
+        * But VFS will not use f->dents after iput() has been called.
+        */
        return 0;
 }
 
-static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd)
-{
-       char *s = nd_get_link(nd);
-       if (!IS_ERR(s))
-               kfree(s);
-}
index d6b4d55e70e4d062bdc706c09c686c98e2497ca5..f3910dc1c2c886815a9a306f10d56144e1504cdb 100644 (file)
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: write.c,v 1.90 2005/01/28 18:53:01 hammache Exp $
+ * $Id: write.c,v 1.91 2005/03/01 10:34:03 dedekind Exp $
  *
  */
 
@@ -644,20 +644,23 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
 
                down(&dead_f->sem);
 
-               while (dead_f->dents) {
-                       /* There can be only deleted ones */
-                       fd = dead_f->dents;
-                       
-                       dead_f->dents = fd->next;
-                       
-                       if (fd->ino) {
-                               printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
-                                      dead_f->inocache->ino, fd->name, fd->ino);
-                       } else {
-                               D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino));
+               if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) {
+                       while (dead_f->dents) {
+                               /* There can be only deleted ones */
+                               fd = dead_f->dents;
+                               
+                               dead_f->dents = fd->next;
+                               
+                               if (fd->ino) {
+                                       printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
+                                              dead_f->inocache->ino, fd->name, fd->ino);
+                               } else {
+                                       D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n",
+                                               fd->name, dead_f->inocache->ino));
+                               }
+                               jffs2_mark_node_obsolete(c, fd->raw);
+                               jffs2_free_full_dirent(fd);
                        }
-                       jffs2_mark_node_obsolete(c, fd->raw);
-                       jffs2_free_full_dirent(fd);
                }
 
                dead_f->inocache->nlink--;