X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs2.git;a=blobdiff_plain;f=yaffs_fs.c;h=ab123f7bd5ee04064a9e1f57d0d02c2f6def73de;hp=dfa94a2955d0bec53a9af4a3d2d31129e2f1b7c6;hb=f1b7e47fc0e0d58c0e4f6e298c0dd50b6f96370d;hpb=859e8e1d2c37dd6ac976640c2d52281002af2c44 diff --git a/yaffs_fs.c b/yaffs_fs.c index dfa94a2..ab123f7 100644 --- a/yaffs_fs.c +++ b/yaffs_fs.c @@ -32,7 +32,7 @@ */ const char *yaffs_fs_c_version = - "$Id: yaffs_fs.c,v 1.87 2009-11-11 02:11:13 charles Exp $"; + "$Id: yaffs_fs.c,v 1.89 2009-11-29 21:50:10 charles Exp $"; extern const char *yaffs_guts_c_version; #include @@ -365,68 +365,135 @@ static const struct super_operations yaffs_super_ops = { .write_super = yaffs_write_super, }; -/* YAFFS uses two locks per yaffs_Device. - * dirLock: r/w lock Must be held when accessing directory structure. - * grossLock: Lock when accessing yaffs internals. - * - * Locking rules: - * If you're going to take dirLock then you must take if before - * taking grossLock. - * ie. Don't call yaffs_DirLockxxx() while holding grossLock. - * - * Todo: - * Investigate changing to mutexes etc and improve debugging. - */ -static void yaffs_DirLockInitialise(yaffs_Device *dev) +static void yaffs_GrossLock(yaffs_Device *dev) { - init_rwsem(&dev->dirLock); + T(YAFFS_TRACE_OS, ("yaffs locking %p\n", current)); + down(&dev->grossLock); + T(YAFFS_TRACE_OS, ("yaffs locked %p\n", current)); } -static void yaffs_DirLockRead(yaffs_Device *dev) +static void yaffs_GrossUnlock(yaffs_Device *dev) { - T(YAFFS_TRACE_OS, ("yaffs locking dir read %p\n", current)); - down_read(&dev->dirLock); - T(YAFFS_TRACE_OS, ("yaffs locked dir read %p\n", current)); + T(YAFFS_TRACE_OS, ("yaffs unlocking %p\n", current)); + up(&dev->grossLock); } -static void yaffs_DirUnlockRead(yaffs_Device *dev) -{ - T(YAFFS_TRACE_OS, ("yaffs unlocking dir read %p\n", current)); - up_read(&dev->dirLock); -} -static void yaffs_DirLockWrite(yaffs_Device *dev) -{ - T(YAFFS_TRACE_OS, ("yaffs locking dir write %p\n", current)); - down_write(&dev->dirLock); - T(YAFFS_TRACE_OS, ("yaffs locked dir write %p\n", current)); -} +/*-----------------------------------------------------------------*/ +/* Directory search context allows us to unlock access to yaffs during + * filldir without causing problems with the directory being modified. + * This is similar to the tried and tested mechanism used in yaffs direct. + * + * A search context iterates along a doubly linked list of siblings in the + * directory. If the iterating object is deleted then this would corrupt + * the list iteration, likely causing a crash. The search context avoids + * this by using the removeObjectCallback to move the search context to the + * next object before the object is deleted. + * + * Many readdirs (and thus seach conexts) may be alive simulateously so + * each yaffs_Device has a list of these. + * + * A seach context lives for the duration of a readdir. + * + * All these functions must be called while yaffs is locked. + */ -static void yaffs_DirUnlockWrite(yaffs_Device *dev) -{ - T(YAFFS_TRACE_OS, ("yaffs unlocking dir write %p\n", current)); - up_write(&dev->dirLock); +struct yaffs_SearchContext { + yaffs_Device *dev; + yaffs_Object *dirObj; + yaffs_Object *nextReturn; + struct ylist_head others; +}; + +/* + * yaffs_NewSearch() creates a new search context, initialises it and + * adds it to the device's search context list. + * + * Called at start of readdir. + */ +static struct yaffs_SearchContext * yaffs_NewSearch(yaffs_Object *dir) +{ + yaffs_Device *dev = dir->myDev; + struct yaffs_SearchContext *sc = YMALLOC(sizeof(struct yaffs_SearchContext)); + if(sc){ + sc->dirObj = dir; + sc->dev = dev; + if( ylist_empty(&sc->dirObj->variant.directoryVariant.children)) + sc->nextReturn = NULL; + else + sc->nextReturn = ylist_entry( + dir->variant.directoryVariant.children.next, + yaffs_Object,siblings); + YINIT_LIST_HEAD(&sc->others); + ylist_add(&sc->others,&dev->searchContexts); + } + return sc; } -static void yaffs_GrossLockInitialise(yaffs_Device *dev) +/* + * yaffs_EndSearch() disposes of a search context and cleans up. + */ +static void yaffs_EndSearch(struct yaffs_SearchContext * sc) { - init_MUTEX(&dev->grossLock); + if(sc){ + ylist_del(&sc->others); + YFREE(sc); + } } -static void yaffs_GrossLock(yaffs_Device *dev) +/* + * yaffs_SearchAdvance() moves a search context to the next object. + * Called when the search iterates or when an object removal causes + * the search context to be moved to the next object. + */ +static void yaffs_SearchAdvance(struct yaffs_SearchContext *sc) { - T(YAFFS_TRACE_OS, ("yaffs locking %p\n", current)); - down(&dev->grossLock); - T(YAFFS_TRACE_OS, ("yaffs locked %p\n", current)); + if(!sc) + return; + + if( sc->nextReturn == NULL || + ylist_empty(&sc->dirObj->variant.directoryVariant.children)) + sc->nextReturn = NULL; + else { + struct ylist_head *next = sc->nextReturn->siblings.next; + + if( next == &sc->dirObj->variant.directoryVariant.children) + sc->nextReturn = NULL; /* end of list */ + else + sc->nextReturn = ylist_entry(next,yaffs_Object,siblings); + } } -static void yaffs_GrossUnlock(yaffs_Device *dev) +/* + * yaffs_RemoveObjectCallback() is called when an object is unlinked. + * We check open search contexts and advance any which are currently + * on the object being iterated. + */ +static void yaffs_RemoveObjectCallback(yaffs_Object *obj) { - T(YAFFS_TRACE_OS, ("yaffs unlocking %p\n", current)); - up(&dev->grossLock); + + struct ylist_head *i; + struct yaffs_SearchContext *sc; + struct ylist_head *search_contexts = &obj->myDev->searchContexts; + + + /* Iterate through the directory search contexts. + * If any are currently on the object being removed, then advance + * the search context to the next object to prevent a hanging pointer. + */ + ylist_for_each(i, search_contexts) { + if (i) { + sc = ylist_entry(i, struct yaffs_SearchContext,others); + if(sc->nextReturn == obj) + yaffs_SearchAdvance(sc); + } + } + } +/*-----------------------------------------------------------------*/ + static int yaffs_readlink(struct dentry *dentry, char __user *buffer, int buflen) { @@ -434,14 +501,12 @@ static int yaffs_readlink(struct dentry *dentry, char __user *buffer, int ret; yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev; - - yaffs_DirLockRead(dev); + yaffs_GrossLock(dev); alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry)); yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); if (!alias) return -ENOMEM; @@ -461,13 +526,11 @@ static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) int ret; yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev; - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry)); yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); if (!alias) { ret = -ENOMEM; @@ -503,7 +566,6 @@ static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) yaffs_Device *dev = yaffs_InodeToObject(dir)->myDev; - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); T(YAFFS_TRACE_OS, @@ -517,7 +579,6 @@ static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) /* Can't hold gross lock when calling yaffs_get_inode() */ yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); if (obj) { T(YAFFS_TRACE_OS, @@ -581,7 +642,6 @@ static void yaffs_clear_inode(struct inode *inode) if (obj) { dev = obj->myDev; - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); /* Clear the association between the inode and @@ -598,7 +658,6 @@ static void yaffs_clear_inode(struct inode *inode) yaffs_HandleDeferedFree(obj); yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); } } @@ -620,11 +679,9 @@ static void yaffs_delete_inode(struct inode *inode) if (obj) { dev = obj->myDev; - yaffs_DirLockWrite(dev); yaffs_GrossLock(dev); yaffs_DeleteObject(obj); yaffs_GrossUnlock(dev); - yaffs_DirUnlockWrite(dev); } #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) truncate_inode_pages(&inode->i_data, 0); @@ -646,13 +703,11 @@ static int yaffs_file_flush(struct file *file) ("yaffs_file_flush object %d (%s)\n", obj->objectId, obj->dirty ? "dirty" : "clean")); - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); yaffs_FlushFile(obj, 1,0); yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); return 0; } @@ -685,7 +740,6 @@ static int yaffs_readpage_nolock(struct file *f, struct page *pg) pg_buf = kmap(pg); /* FIXME: Can kmap fail? */ - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); ret = yaffs_ReadDataFromFile(obj, pg_buf, @@ -693,7 +747,6 @@ static int yaffs_readpage_nolock(struct file *f, struct page *pg) PAGE_CACHE_SIZE); yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); if (ret >= 0) ret = 0; @@ -772,7 +825,6 @@ static int yaffs_writepage(struct page *page) buffer = kmap(page); obj = yaffs_InodeToObject(inode); - yaffs_DirLockRead(obj->myDev); yaffs_GrossLock(obj->myDev); T(YAFFS_TRACE_OS, @@ -790,7 +842,6 @@ static int yaffs_writepage(struct page *page) (int)obj->variant.fileVariant.fileSize, (int)inode->i_size)); yaffs_GrossUnlock(obj->myDev); - yaffs_DirUnlockRead(obj->myDev); kunmap(page); SetPageUptodate(page); @@ -1106,7 +1157,6 @@ static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, dev = obj->myDev; - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); inode = f->f_dentry->d_inode; @@ -1146,8 +1196,6 @@ static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, } yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); - return (nWritten == 0) && (n > 0) ? -ENOSPC : nWritten; } @@ -1167,54 +1215,66 @@ static ssize_t yaffs_hold_space(struct file *f) dev = obj->myDev; - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); nFreeChunks = yaffs_GetNumberOfFreeChunks(dev); yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); return (nFreeChunks > 20) ? 1 : 0; } static void yaffs_release_space(struct file *f) { + yaffs_Object *obj; + yaffs_Device *dev; + + + obj = yaffs_DentryToObject(f->f_dentry); + + dev = obj->myDev; + + yaffs_GrossLock(dev); + + + yaffs_GrossUnlock(dev); } static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) { yaffs_Object *obj; yaffs_Device *dev; + struct yaffs_SearchContext *sc; struct inode *inode = f->f_dentry->d_inode; unsigned long offset, curoffs; - struct ylist_head *i; yaffs_Object *l; + int retVal = 0; char name[YAFFS_MAX_NAME_LENGTH + 1]; obj = yaffs_DentryToObject(f->f_dentry); dev = obj->myDev; - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); offset = f->f_pos; + sc = yaffs_NewSearch(obj); + if(!sc){ + retVal = -ENOMEM; + goto unlock_out; + } + T(YAFFS_TRACE_OS, ("yaffs_readdir: starting at %d\n", (int)offset)); if (offset == 0) { T(YAFFS_TRACE_OS, ("yaffs_readdir: entry . ino %d \n", (int)inode->i_ino)); - yaffs_GrossUnlock(dev); - if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) goto out; - yaffs_GrossLock(dev); - offset++; f->f_pos++; } @@ -1222,15 +1282,11 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) T(YAFFS_TRACE_OS, ("yaffs_readdir: entry .. ino %d \n", (int)f->f_dentry->d_parent->d_inode->i_ino)); - yaffs_GrossUnlock(dev); - if (filldir(dirent, "..", 2, offset, f->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) goto out; - yaffs_GrossLock(dev); - offset++; f->f_pos++; } @@ -1246,10 +1302,12 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) f->f_version = inode->i_version; } - ylist_for_each(i, &obj->variant.directoryVariant.children) { + while(sc->nextReturn){ curoffs++; + l = sc->nextReturn; if (curoffs >= offset) { - l = ylist_entry(i, yaffs_Object, siblings); + int this_inode = yaffs_GetObjectInode(l); + int this_type = yaffs_GetObjectType(l); yaffs_GetObjectName(l, name, YAFFS_MAX_NAME_LENGTH + 1); @@ -1257,28 +1315,30 @@ static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) ("yaffs_readdir: %s inode %d\n", name, yaffs_GetObjectInode(l))); - yaffs_GrossUnlock(dev); + yaffs_GrossUnlock(dev); if (filldir(dirent, name, strlen(name), offset, - yaffs_GetObjectInode(l), - yaffs_GetObjectType(l)) < 0) + this_inode, + this_type) < 0) goto out; - yaffs_GrossLock(dev); + yaffs_GrossLock(dev); offset++; f->f_pos++; } + yaffs_SearchAdvance(sc); } +unlock_out: yaffs_GrossUnlock(dev); out: - yaffs_DirUnlockRead(dev); + yaffs_EndSearch(sc); - return 0; + return retVal; } /* @@ -1329,7 +1389,6 @@ static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev = parent->myDev; - yaffs_DirLockWrite(dev); yaffs_GrossLock(dev); switch (mode & S_IFMT) { @@ -1363,7 +1422,6 @@ static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, /* Can not call yaffs_get_inode() with gross lock held */ yaffs_GrossUnlock(dev); - yaffs_DirUnlockWrite(dev); if (obj) { inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); @@ -1413,7 +1471,6 @@ static int yaffs_unlink(struct inode *dir, struct dentry *dentry) dev = yaffs_InodeToObject(dir)->myDev; - yaffs_DirLockWrite(dev); yaffs_GrossLock(dev); retVal = yaffs_Unlink(yaffs_InodeToObject(dir), dentry->d_name.name); @@ -1421,17 +1478,12 @@ static int yaffs_unlink(struct inode *dir, struct dentry *dentry) if (retVal == YAFFS_OK) { dentry->d_inode->i_nlink--; dir->i_version++; - } - - yaffs_GrossUnlock(dev); - yaffs_DirUnlockWrite(dev); - - if( retVal == YAFFS_OK){ + yaffs_GrossUnlock(dev); mark_inode_dirty(dentry->d_inode); update_dir_time(dir); return 0; } - + yaffs_GrossUnlock(dev); return -ENOTEMPTY; } @@ -1451,7 +1503,6 @@ static int yaffs_link(struct dentry *old_dentry, struct inode *dir, obj = yaffs_InodeToObject(inode); dev = obj->myDev; - yaffs_DirLockWrite(dev); yaffs_GrossLock(dev); if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ @@ -1469,7 +1520,6 @@ static int yaffs_link(struct dentry *old_dentry, struct inode *dir, } yaffs_GrossUnlock(dev); - yaffs_DirUnlockWrite(dev); if (link){ update_dir_time(dir); @@ -1490,12 +1540,10 @@ static int yaffs_symlink(struct inode *dir, struct dentry *dentry, T(YAFFS_TRACE_OS, ("yaffs_symlink\n")); dev = yaffs_InodeToObject(dir)->myDev; - yaffs_DirLockWrite(dev); yaffs_GrossLock(dev); obj = yaffs_MknodSymLink(yaffs_InodeToObject(dir), dentry->d_name.name, S_IFLNK | S_IRWXUGO, uid, gid, symname); yaffs_GrossUnlock(dev); - yaffs_DirUnlockWrite(dev); if (obj) { struct inode *inode; @@ -1524,11 +1572,9 @@ static int yaffs_sync_object(struct file *file, struct dentry *dentry, dev = obj->myDev; T(YAFFS_TRACE_OS, ("yaffs_sync_object\n")); - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); yaffs_FlushFile(obj, 1, datasync); yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); return 0; } @@ -1547,7 +1593,6 @@ static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, T(YAFFS_TRACE_OS, ("yaffs_rename\n")); dev = yaffs_InodeToObject(old_dir)->myDev; - yaffs_DirLockWrite(dev); yaffs_GrossLock(dev); /* Check if the target is an existing directory that is not empty. */ @@ -1572,7 +1617,6 @@ static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, new_dentry->d_name.name); } yaffs_GrossUnlock(dev); - yaffs_DirUnlockWrite(dev); if (retVal == YAFFS_OK) { if (target) { @@ -1602,7 +1646,6 @@ static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) error = inode_change_ok(inode, attr); if (error == 0) { dev = yaffs_InodeToObject(inode)->myDev; - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); if (yaffs_SetAttributes(yaffs_InodeToObject(inode), attr) == YAFFS_OK) { @@ -1611,7 +1654,6 @@ static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) error = -EPERM; } yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); if (!error) error = inode_setattr(inode, attr); } @@ -1635,7 +1677,6 @@ static int yaffs_statfs(struct super_block *sb, struct statfs *buf) T(YAFFS_TRACE_OS, ("yaffs_statfs\n")); - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); buf->f_type = YAFFS_MAGIC; @@ -1686,7 +1727,6 @@ static int yaffs_statfs(struct super_block *sb, struct statfs *buf) buf->f_bavail = buf->f_bfree; yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); return 0; } @@ -1713,7 +1753,6 @@ static int yaffs_do_sync_fs(struct super_block *sb) T(YAFFS_TRACE_OS, ("yaffs_do_sync_fs\n")); if (sb->s_dirt) { - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); if (dev) { @@ -1723,7 +1762,6 @@ static int yaffs_do_sync_fs(struct super_block *sb) } yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); sb->s_dirt = 0; } @@ -1783,7 +1821,6 @@ static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) * need to lock again. */ - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); obj = yaffs_FindObjectByNumber(dev, inode->i_ino); @@ -1791,7 +1828,6 @@ static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) yaffs_FillInodeFromObject(inode, obj); yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); unlock_new_inode(inode); return inode; @@ -1812,7 +1848,6 @@ static void yaffs_read_inode(struct inode *inode) T(YAFFS_TRACE_OS, ("yaffs_read_inode for %d\n", (int)inode->i_ino)); - yaffs_DirLockRead(dev); yaffs_GrossLock(dev); obj = yaffs_FindObjectByNumber(dev, inode->i_ino); @@ -1820,7 +1855,6 @@ static void yaffs_read_inode(struct inode *inode) yaffs_FillInodeFromObject(inode, obj); yaffs_GrossUnlock(dev); - yaffs_DirUnlockRead(dev); } #endif @@ -1837,7 +1871,7 @@ static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) T(YAFFS_TRACE_OS, ("yaffs_remount_fs: %s: RO\n", dev->name)); - yaffs_DirLockWrite(dev); + yaffs_GrossLock(dev); yaffs_FlushEntireDeviceCache(dev); @@ -1848,7 +1882,6 @@ static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) mtd->sync(mtd); yaffs_GrossUnlock(dev); - yaffs_DirUnlockWrite(dev); } else { T(YAFFS_TRACE_OS, ("yaffs_remount_fs: %s: RW\n", dev->name)); @@ -1864,7 +1897,6 @@ static void yaffs_put_super(struct super_block *sb) T(YAFFS_TRACE_OS, ("yaffs_put_super\n")); - yaffs_DirLockWrite(dev); yaffs_GrossLock(dev); yaffs_FlushEntireDeviceCache(dev); @@ -1877,7 +1909,6 @@ static void yaffs_put_super(struct super_block *sb) yaffs_Deinitialise(dev); yaffs_GrossUnlock(dev); - yaffs_DirUnlockWrite(dev); /* we assume this is protected by lock_kernel() in mount/umount */ ylist_del(&dev->devList); @@ -2261,11 +2292,13 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, /* we assume this is protected by lock_kernel() in mount/umount */ ylist_add_tail(&dev->devList, &yaffs_dev_list); - - yaffs_DirLockInitialise(dev); - yaffs_GrossLockInitialise(dev); - yaffs_DirLockWrite(dev); + /* Directory search handling...*/ + YINIT_LIST_HEAD(&dev->searchContexts); + dev->removeObjectCallback = yaffs_RemoveObjectCallback; + + init_MUTEX(&dev->grossLock); + yaffs_GrossLock(dev); err = yaffs_GutsInitialise(dev); @@ -2276,7 +2309,6 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, /* Release lock before yaffs_get_inode() */ yaffs_GrossUnlock(dev); - yaffs_DirUnlockWrite(dev); /* Create root inode */ if (err == YAFFS_OK)