From 9d0243bca345d5ce25d3f4b74b7facb3a6df1232 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 8 Jan 2006 01:00:39 -0800 Subject: [PATCH] drop-pagecache Add /proc/sys/vm/drop_caches. When written to, this will cause the kernel to discard as much pagecache and/or reclaimable slab objects as it can. THis operation requires root permissions. It won't drop dirty data, so the user should run `sync' first. Caveats: a) Holds inode_lock for exorbitant amounts of time. b) Needs to be taught about NUMA nodes: propagate these all the way through so the discarding can be controlled on a per-node basis. This is a debugging feature: useful for getting consistent results between filesystem benchmarks. We could possibly put it under a config option, but it's less than 300 bytes. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/Makefile | 2 +- fs/drop_caches.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 fs/drop_caches.c (limited to 'fs') diff --git a/fs/Makefile b/fs/Makefile index 73676111ebb..35e9aec608e 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -10,7 +10,7 @@ obj-y := open.o read_write.o file_table.o buffer.o bio.o super.o \ ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \ attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \ seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \ - ioprio.o pnode.o + ioprio.o pnode.o drop_caches.o obj-$(CONFIG_INOTIFY) += inotify.o obj-$(CONFIG_EPOLL) += eventpoll.o diff --git a/fs/drop_caches.c b/fs/drop_caches.c new file mode 100644 index 00000000000..4e4762389bd --- /dev/null +++ b/fs/drop_caches.c @@ -0,0 +1,68 @@ +/* + * Implement the manual drop-all-pagecache function + */ + +#include +#include +#include +#include +#include +#include + +/* A global variable is a bit ugly, but it keeps the code simple */ +int sysctl_drop_caches; + +static void drop_pagecache_sb(struct super_block *sb) +{ + struct inode *inode; + + spin_lock(&inode_lock); + list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { + if (inode->i_state & (I_FREEING|I_WILL_FREE)) + continue; + invalidate_inode_pages(inode->i_mapping); + } + spin_unlock(&inode_lock); +} + +void drop_pagecache(void) +{ + struct super_block *sb; + + spin_lock(&sb_lock); +restart: + list_for_each_entry(sb, &super_blocks, s_list) { + sb->s_count++; + spin_unlock(&sb_lock); + down_read(&sb->s_umount); + if (sb->s_root) + drop_pagecache_sb(sb); + up_read(&sb->s_umount); + spin_lock(&sb_lock); + if (__put_super_and_need_restart(sb)) + goto restart; + } + spin_unlock(&sb_lock); +} + +void drop_slab(void) +{ + int nr_objects; + + do { + nr_objects = shrink_slab(1000, GFP_KERNEL, 1000); + } while (nr_objects > 10); +} + +int drop_caches_sysctl_handler(ctl_table *table, int write, + struct file *file, void __user *buffer, size_t *length, loff_t *ppos) +{ + proc_dointvec_minmax(table, write, file, buffer, length, ppos); + if (write) { + if (sysctl_drop_caches & 1) + drop_pagecache(); + if (sysctl_drop_caches & 2) + drop_slab(); + } + return 0; +} -- cgit From 1a75a6c825c17249ca49f050a872a04ce0997ce3 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Sun, 8 Jan 2006 01:01:02 -0800 Subject: [PATCH] Fold numa_maps into mempolicies.c First discussed at http://marc.theaimsgroup.com/?t=113149255100001&r=1&w=2 - Use the check_range() in mempolicy.c to gather statistics. - Improve the numa_maps code in general and fix some comments. Signed-off-by: Christoph Lameter Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/task_mmu.c | 127 +++-------------------------------------------------- 1 file changed, 5 insertions(+), 122 deletions(-) (limited to 'fs') diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 50bd5a8f044..0eaad41f465 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -390,129 +390,12 @@ struct seq_operations proc_pid_smaps_op = { }; #ifdef CONFIG_NUMA - -struct numa_maps { - unsigned long pages; - unsigned long anon; - unsigned long mapped; - unsigned long mapcount_max; - unsigned long node[MAX_NUMNODES]; -}; - -/* - * Calculate numa node maps for a vma - */ -static struct numa_maps *get_numa_maps(struct vm_area_struct *vma) -{ - int i; - struct page *page; - unsigned long vaddr; - struct numa_maps *md = kmalloc(sizeof(struct numa_maps), GFP_KERNEL); - - if (!md) - return NULL; - md->pages = 0; - md->anon = 0; - md->mapped = 0; - md->mapcount_max = 0; - for_each_node(i) - md->node[i] =0; - - for (vaddr = vma->vm_start; vaddr < vma->vm_end; vaddr += PAGE_SIZE) { - page = follow_page(vma, vaddr, 0); - if (page) { - int count = page_mapcount(page); - - if (count) - md->mapped++; - if (count > md->mapcount_max) - md->mapcount_max = count; - md->pages++; - if (PageAnon(page)) - md->anon++; - md->node[page_to_nid(page)]++; - } - cond_resched(); - } - return md; -} - -static int show_numa_map(struct seq_file *m, void *v) -{ - struct task_struct *task = m->private; - struct vm_area_struct *vma = v; - struct mempolicy *pol; - struct numa_maps *md; - struct zone **z; - int n; - int first; - - if (!vma->vm_mm) - return 0; - - md = get_numa_maps(vma); - if (!md) - return 0; - - seq_printf(m, "%08lx", vma->vm_start); - pol = get_vma_policy(task, vma, vma->vm_start); - /* Print policy */ - switch (pol->policy) { - case MPOL_PREFERRED: - seq_printf(m, " prefer=%d", pol->v.preferred_node); - break; - case MPOL_BIND: - seq_printf(m, " bind={"); - first = 1; - for (z = pol->v.zonelist->zones; *z; z++) { - - if (!first) - seq_putc(m, ','); - else - first = 0; - seq_printf(m, "%d/%s", (*z)->zone_pgdat->node_id, - (*z)->name); - } - seq_putc(m, '}'); - break; - case MPOL_INTERLEAVE: - seq_printf(m, " interleave={"); - first = 1; - for_each_node(n) { - if (node_isset(n, pol->v.nodes)) { - if (!first) - seq_putc(m,','); - else - first = 0; - seq_printf(m, "%d",n); - } - } - seq_putc(m, '}'); - break; - default: - seq_printf(m," default"); - break; - } - seq_printf(m, " MaxRef=%lu Pages=%lu Mapped=%lu", - md->mapcount_max, md->pages, md->mapped); - if (md->anon) - seq_printf(m," Anon=%lu",md->anon); - - for_each_online_node(n) { - if (md->node[n]) - seq_printf(m, " N%d=%lu", n, md->node[n]); - } - seq_putc(m, '\n'); - kfree(md); - if (m->count < m->size) /* vma is copied successfully */ - m->version = (vma != get_gate_vma(task)) ? vma->vm_start : 0; - return 0; -} +extern int show_numa_map(struct seq_file *m, void *v); struct seq_operations proc_pid_numa_maps_op = { - .start = m_start, - .next = m_next, - .stop = m_stop, - .show = show_numa_map + .start = m_start, + .next = m_next, + .stop = m_stop, + .show = show_numa_map }; #endif -- cgit From 2919b51075b3906c2f476e5a932244af1947bf80 Mon Sep 17 00:00:00 2001 From: David Howells Date: Sun, 8 Jan 2006 01:01:16 -0800 Subject: [PATCH] frv: suppress configuration of certain features for FRV Suppress configuration of certain features for the FRV arch as they can't be built for FRV at the moment: (*) RTC (*) HISAX_* (*) PARPORT_PC (*) VGA_CONSOLE (*) BINFMT_ELF Signed-off-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/Kconfig.binfmt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt index 175b2e8177c..f3d3d81eb7e 100644 --- a/fs/Kconfig.binfmt +++ b/fs/Kconfig.binfmt @@ -1,6 +1,6 @@ config BINFMT_ELF bool "Kernel support for ELF binaries" - depends on MMU + depends on MMU && (BROKEN || !FRV) default y ---help--- ELF (Executable and Linkable Format) is a format for libraries and -- cgit From e56d090310d7625ecb43a1eeebd479f04affb48b Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sun, 8 Jan 2006 01:01:37 -0800 Subject: [PATCH] RCU signal handling RCU tasklist_lock and RCU signal handling: send signals RCU-read-locked instead of tasklist_lock read-locked. This is a scalability improvement on SMP and a preemption-latency improvement under PREEMPT_RCU. Signed-off-by: Paul E. McKenney Signed-off-by: Ingo Molnar Acked-by: William Irwin Cc: Roland McGrath Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/exec.c b/fs/exec.c index e75a9548da8..e9650cd22a3 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -760,7 +760,7 @@ no_thread_group: spin_lock(&oldsighand->siglock); spin_lock(&newsighand->siglock); - current->sighand = newsighand; + rcu_assign_pointer(current->sighand, newsighand); recalc_sigpending(); spin_unlock(&newsighand->siglock); @@ -768,7 +768,7 @@ no_thread_group: write_unlock_irq(&tasklist_lock); if (atomic_dec_and_test(&oldsighand->count)) - kmem_cache_free(sighand_cachep, oldsighand); + sighand_free(oldsighand); } BUG_ON(!thread_group_leader(current)); -- cgit From 10cef6029502915bdb3cf0821d425cf9dc30c817 Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sun, 8 Jan 2006 01:01:45 -0800 Subject: [PATCH] slob: introduce the SLOB allocator configurable replacement for slab allocator This adds a CONFIG_SLAB option under CONFIG_EMBEDDED. When CONFIG_SLAB is disabled, the kernel falls back to using the 'SLOB' allocator. SLOB is a traditional K&R/UNIX allocator with a SLAB emulation layer, similar to the original Linux kmalloc allocator that SLAB replaced. It's signicantly smaller code and is more memory efficient. But like all similar allocators, it scales poorly and suffers from fragmentation more than SLAB, so it's only appropriate for small systems. It's been tested extensively in the Linux-tiny tree. I've also stress-tested it with make -j 8 compiles on a 3G SMP+PREEMPT box (not recommended). Here's a comparison for otherwise identical builds, showing SLOB saving nearly half a megabyte of RAM: $ size vmlinux* text data bss dec hex filename 3336372 529360 190812 4056544 3de5e0 vmlinux-slab 3323208 527948 190684 4041840 3dac70 vmlinux-slob $ size mm/{slab,slob}.o text data bss dec hex filename 13221 752 48 14021 36c5 mm/slab.o 1896 52 8 1956 7a4 mm/slob.o /proc/meminfo: SLAB SLOB delta MemTotal: 27964 kB 27980 kB +16 kB MemFree: 24596 kB 25092 kB +496 kB Buffers: 36 kB 36 kB 0 kB Cached: 1188 kB 1188 kB 0 kB SwapCached: 0 kB 0 kB 0 kB Active: 608 kB 600 kB -8 kB Inactive: 808 kB 812 kB +4 kB HighTotal: 0 kB 0 kB 0 kB HighFree: 0 kB 0 kB 0 kB LowTotal: 27964 kB 27980 kB +16 kB LowFree: 24596 kB 25092 kB +496 kB SwapTotal: 0 kB 0 kB 0 kB SwapFree: 0 kB 0 kB 0 kB Dirty: 4 kB 12 kB +8 kB Writeback: 0 kB 0 kB 0 kB Mapped: 560 kB 556 kB -4 kB Slab: 1756 kB 0 kB -1756 kB CommitLimit: 13980 kB 13988 kB +8 kB Committed_AS: 4208 kB 4208 kB 0 kB PageTables: 28 kB 28 kB 0 kB VmallocTotal: 1007312 kB 1007312 kB 0 kB VmallocUsed: 48 kB 48 kB 0 kB VmallocChunk: 1007264 kB 1007264 kB 0 kB (this work has been sponsored in part by CELF) From: Ingo Molnar Fix 32-bitness bugs in mm/slob.c. Signed-off-by: Matt Mackall Signed-off-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/proc_misc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs') diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 5b6b0b6038a..63bf6c00fa0 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -323,6 +323,7 @@ static struct file_operations proc_modules_operations = { }; #endif +#ifdef CONFIG_SLAB extern struct seq_operations slabinfo_op; extern ssize_t slabinfo_write(struct file *, const char __user *, size_t, loff_t *); static int slabinfo_open(struct inode *inode, struct file *file) @@ -336,6 +337,7 @@ static struct file_operations proc_slabinfo_operations = { .llseek = seq_lseek, .release = seq_release, }; +#endif static int show_stat(struct seq_file *p, void *v) { @@ -600,7 +602,9 @@ void __init proc_misc_init(void) create_seq_entry("partitions", 0, &proc_partitions_operations); create_seq_entry("stat", 0, &proc_stat_operations); create_seq_entry("interrupts", 0, &proc_interrupts_operations); +#ifdef CONFIG_SLAB create_seq_entry("slabinfo",S_IWUSR|S_IRUGO,&proc_slabinfo_operations); +#endif create_seq_entry("buddyinfo",S_IRUGO, &fragmentation_file_operations); create_seq_entry("vmstat",S_IRUGO, &proc_vmstat_file_operations); create_seq_entry("zoneinfo",S_IRUGO, &proc_zoneinfo_file_operations); -- cgit From a6bf6b211cdb92c315c24719a522d8b6f3998210 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Sun, 8 Jan 2006 01:02:08 -0800 Subject: [PATCH] fat: move fat_clusters_flush() to write_super() It is overkill to update the FS_INFO whenever modifying prev_free/free_clusters, because those are just a hint. So, this patch uses ->write_super() for updating FS_INFO instead. Signed-off-by: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fat/fatent.c | 8 ++++++-- fs/fat/inode.c | 10 ++++++++-- fs/fat/misc.c | 2 -- 3 files changed, 14 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index 4164cd54c4d..20a22875f0a 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -476,6 +476,7 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) sbi->prev_free = entry; if (sbi->free_clusters != -1) sbi->free_clusters--; + sb->s_dirt = 1; cluster[idx_clus] = entry; idx_clus++; @@ -496,6 +497,7 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) /* Couldn't allocate the free entries */ sbi->free_clusters = 0; + sb->s_dirt = 1; err = -ENOSPC; out: @@ -509,7 +511,6 @@ out: } for (i = 0; i < nr_bhs; i++) brelse(bhs[i]); - fat_clusters_flush(sb); if (err && idx_clus) fat_free_clusters(inode, cluster[0]); @@ -542,8 +543,10 @@ int fat_free_clusters(struct inode *inode, int cluster) } ops->ent_put(&fatent, FAT_ENT_FREE); - if (sbi->free_clusters != -1) + if (sbi->free_clusters != -1) { sbi->free_clusters++; + sb->s_dirt = 1; + } if (nr_bhs + fatent.nr_bhs > MAX_BUF_PER_PAGE) { if (sb->s_flags & MS_SYNCHRONOUS) { @@ -605,6 +608,7 @@ int fat_count_free_clusters(struct super_block *sb) } while (fat_ent_next(sbi, &fatent)); } sbi->free_clusters = free; + sb->s_dirt = 1; fatent_brelse(&fatent); out: unlock_fat(sbi); diff --git a/fs/fat/inode.c b/fs/fat/inode.c index a0f9b9fe130..897312616b0 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -374,12 +374,17 @@ static void fat_clear_inode(struct inode *inode) unlock_kernel(); } -static void fat_put_super(struct super_block *sb) +static void fat_write_super(struct super_block *sb) { - struct msdos_sb_info *sbi = MSDOS_SB(sb); + sb->s_dirt = 0; if (!(sb->s_flags & MS_RDONLY)) fat_clusters_flush(sb); +} + +static void fat_put_super(struct super_block *sb) +{ + struct msdos_sb_info *sbi = MSDOS_SB(sb); if (sbi->nls_disk) { unload_nls(sbi->nls_disk); @@ -546,6 +551,7 @@ static struct super_operations fat_sops = { .write_inode = fat_write_inode, .delete_inode = fat_delete_inode, .put_super = fat_put_super, + .write_super = fat_write_super, .statfs = fat_statfs, .clear_inode = fat_clear_inode, .remount_fs = fat_remount, diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 2a0df2122f5..9b592e37e25 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -67,8 +67,6 @@ void fat_clusters_flush(struct super_block *sb) if (sbi->prev_free != -1) fsinfo->next_cluster = cpu_to_le32(sbi->prev_free); mark_buffer_dirty(bh); - if (sb->s_flags & MS_SYNCHRONOUS) - sync_dirty_buffer(bh); } brelse(bh); } -- cgit From 83b7c996dc859c7b53f94d46ee5c5929cc0399e2 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Sun, 8 Jan 2006 01:02:09 -0800 Subject: [PATCH] fat: use sb_find_get_block() instead of sb_getblk() We don't need to allocate buffer for checking the buffer is uptodate. This use sb_find_get_block() instead, and if it returns NULL it's not uptodate. Signed-off-by: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fat/dir.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/fat/dir.c b/fs/fat/dir.c index ba824964b9b..b2a26cd226b 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -45,8 +45,8 @@ static inline void fat_dir_readahead(struct inode *dir, sector_t iblock, if ((sbi->fat_bits != 32) && (dir->i_ino == MSDOS_ROOT_INO)) return; - bh = sb_getblk(sb, phys); - if (bh && !buffer_uptodate(bh)) { + bh = sb_find_get_block(sb, phys); + if (bh == NULL || !buffer_uptodate(bh)) { for (sec = 0; sec < sbi->sec_per_clus; sec++) sb_breadahead(sb, phys + sec); } -- cgit From a5425d2927a6a771f9ae8767b6bfb3c09225bcdd Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Sun, 8 Jan 2006 01:02:10 -0800 Subject: [PATCH] fat: add the read/writepages() Signed-off-by: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fat/inode.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 897312616b0..f502c6b8cb4 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -90,9 +91,21 @@ static int fat_writepage(struct page *page, struct writeback_control *wbc) return block_write_full_page(page, fat_get_block, wbc); } +static int fat_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + return mpage_writepages(mapping, wbc, fat_get_block); +} + static int fat_readpage(struct file *file, struct page *page) { - return block_read_full_page(page, fat_get_block); + return mpage_readpage(page, fat_get_block); +} + +static int fat_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned nr_pages) +{ + return mpage_readpages(mapping, pages, nr_pages, fat_get_block); } static int fat_prepare_write(struct file *file, struct page *page, @@ -122,7 +135,9 @@ static sector_t _fat_bmap(struct address_space *mapping, sector_t block) static struct address_space_operations fat_aops = { .readpage = fat_readpage, + .readpages = fat_readpages, .writepage = fat_writepage, + .writepages = fat_writepages, .sync_page = block_sync_page, .prepare_write = fat_prepare_write, .commit_write = fat_commit_write, -- cgit From 7c709d00d614d0f2b6a80895b2a1aedbe04e8478 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Sun, 8 Jan 2006 01:02:10 -0800 Subject: [PATCH] fat: s/EXPORT_SYMBOL/EXPORT_SYMBOL_GPL/ All EXPORT_SYMBOL of fatfs is only for vfat/msdos. _GPL would be proper. Signed-off-by: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fat/dir.c | 14 +++++++------- fs/fat/fatent.c | 2 +- fs/fat/file.c | 2 +- fs/fat/inode.c | 10 +++++----- fs/fat/misc.c | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/fat/dir.c b/fs/fat/dir.c index b2a26cd226b..4ce77475aed 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -418,7 +418,7 @@ EODir: return err; } -EXPORT_SYMBOL(fat_search_long); +EXPORT_SYMBOL_GPL(fat_search_long); struct fat_ioctl_filldir_callback { struct dirent __user *dirent; @@ -780,7 +780,7 @@ int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, return -ENOENT; } -EXPORT_SYMBOL(fat_get_dotdot_entry); +EXPORT_SYMBOL_GPL(fat_get_dotdot_entry); /* See if directory is empty */ int fat_dir_empty(struct inode *dir) @@ -803,7 +803,7 @@ int fat_dir_empty(struct inode *dir) return result; } -EXPORT_SYMBOL(fat_dir_empty); +EXPORT_SYMBOL_GPL(fat_dir_empty); /* * fat_subdirs counts the number of sub-directories of dir. It can be run @@ -849,7 +849,7 @@ int fat_scan(struct inode *dir, const unsigned char *name, return -ENOENT; } -EXPORT_SYMBOL(fat_scan); +EXPORT_SYMBOL_GPL(fat_scan); static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots) { @@ -936,7 +936,7 @@ int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo) return 0; } -EXPORT_SYMBOL(fat_remove_entries); +EXPORT_SYMBOL_GPL(fat_remove_entries); static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used, struct buffer_head **bhs, int nr_bhs) @@ -1048,7 +1048,7 @@ error: return err; } -EXPORT_SYMBOL(fat_alloc_new_dir); +EXPORT_SYMBOL_GPL(fat_alloc_new_dir); static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots, int *nr_cluster, struct msdos_dir_entry **de, @@ -1264,4 +1264,4 @@ error_remove: return err; } -EXPORT_SYMBOL(fat_add_entries); +EXPORT_SYMBOL_GPL(fat_add_entries); diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index 20a22875f0a..a1a9e045121 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -581,7 +581,7 @@ error: return err; } -EXPORT_SYMBOL(fat_free_clusters); +EXPORT_SYMBOL_GPL(fat_free_clusters); int fat_count_free_clusters(struct super_block *sb) { diff --git a/fs/fat/file.c b/fs/fat/file.c index 7134403d5be..15229fe569c 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -173,7 +173,7 @@ out: return error; } -EXPORT_SYMBOL(fat_notify_change); +EXPORT_SYMBOL_GPL(fat_notify_change); /* Free all clusters after the skip'th cluster. */ static int fat_free(struct inode *inode, int skip) diff --git a/fs/fat/inode.c b/fs/fat/inode.c index f502c6b8cb4..932c8d6d1f5 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -197,7 +197,7 @@ void fat_attach(struct inode *inode, loff_t i_pos) spin_unlock(&sbi->inode_hash_lock); } -EXPORT_SYMBOL(fat_attach); +EXPORT_SYMBOL_GPL(fat_attach); void fat_detach(struct inode *inode) { @@ -208,7 +208,7 @@ void fat_detach(struct inode *inode) spin_unlock(&sbi->inode_hash_lock); } -EXPORT_SYMBOL(fat_detach); +EXPORT_SYMBOL_GPL(fat_detach); struct inode *fat_iget(struct super_block *sb, loff_t i_pos) { @@ -362,7 +362,7 @@ out: return inode; } -EXPORT_SYMBOL(fat_build_inode); +EXPORT_SYMBOL_GPL(fat_build_inode); static void fat_delete_inode(struct inode *inode) { @@ -557,7 +557,7 @@ int fat_sync_inode(struct inode *inode) return fat_write_inode(inode, 1); } -EXPORT_SYMBOL(fat_sync_inode); +EXPORT_SYMBOL_GPL(fat_sync_inode); static int fat_show_options(struct seq_file *m, struct vfsmount *mnt); static struct super_operations fat_sops = { @@ -1368,7 +1368,7 @@ out_fail: return error; } -EXPORT_SYMBOL(fat_fill_super); +EXPORT_SYMBOL_GPL(fat_fill_super); int __init fat_cache_init(void); void fat_cache_destroy(void); diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 9b592e37e25..32fb0a3f1da 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -33,7 +33,7 @@ void fat_fs_panic(struct super_block *s, const char *fmt, ...) } } -EXPORT_SYMBOL(fat_fs_panic); +EXPORT_SYMBOL_GPL(fat_fs_panic); /* Flushes the number of free clusters on FAT32 */ /* XXX: Need to write one per FSINFO block. Currently only writes 1 */ @@ -192,7 +192,7 @@ void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date) *date = cpu_to_le16(nl_day-day_n[month-1]+1+(month << 5)+(year << 9)); } -EXPORT_SYMBOL(fat_date_unix2dos); +EXPORT_SYMBOL_GPL(fat_date_unix2dos); int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs) { @@ -220,4 +220,4 @@ int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs) return err; } -EXPORT_SYMBOL(fat_sync_bhs); +EXPORT_SYMBOL_GPL(fat_sync_bhs); -- cgit From e5174baaea7585760f02eef23b225847d209a8db Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Sun, 8 Jan 2006 01:02:11 -0800 Subject: [PATCH] fat: support ->direct_IO() This patch add to support of ->direct_IO() for mostly read. The user of this seems to want to use for streaming read. So, current direct I/O has limitation, it can only overwrite. (For write operation, mainly we need to handle the hole etc..) Signed-off-by: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fat/cache.c | 14 +++++++--- fs/fat/dir.c | 6 ++--- fs/fat/inode.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 87 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 77c24fcf712..1acc941245f 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -295,7 +295,8 @@ static int fat_bmap_cluster(struct inode *inode, int cluster) return dclus; } -int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys) +int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, + unsigned long *mapped_blocks) { struct super_block *sb = inode->i_sb; struct msdos_sb_info *sbi = MSDOS_SB(sb); @@ -303,9 +304,12 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys) int cluster, offset; *phys = 0; + *mapped_blocks = 0; if ((sbi->fat_bits != 32) && (inode->i_ino == MSDOS_ROOT_INO)) { - if (sector < (sbi->dir_entries >> sbi->dir_per_block_bits)) + if (sector < (sbi->dir_entries >> sbi->dir_per_block_bits)) { *phys = sector + sbi->dir_start; + *mapped_blocks = 1; + } return 0; } last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1)) @@ -318,7 +322,11 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys) cluster = fat_bmap_cluster(inode, cluster); if (cluster < 0) return cluster; - else if (cluster) + else if (cluster) { *phys = fat_clus_to_blknr(sbi, cluster) + offset; + *mapped_blocks = sbi->sec_per_clus - offset; + if (*mapped_blocks > last_block - sector) + *mapped_blocks = last_block - sector; + } return 0; } diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 4ce77475aed..eef1b81aa29 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -68,8 +68,8 @@ static int fat__get_entry(struct inode *dir, loff_t *pos, { struct super_block *sb = dir->i_sb; sector_t phys, iblock; - int offset; - int err; + unsigned long mapped_blocks; + int err, offset; next: if (*bh) @@ -77,7 +77,7 @@ next: *bh = NULL; iblock = *pos >> sb->s_blocksize_bits; - err = fat_bmap(dir, iblock, &phys); + err = fat_bmap(dir, iblock, &phys, &mapped_blocks); if (err || !phys) return -1; /* beyond EOF or error */ diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 932c8d6d1f5..e7f4aa7fc68 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #ifndef CONFIG_FAT_DEFAULT_IOCHARSET @@ -49,43 +50,77 @@ static int fat_add_cluster(struct inode *inode) return err; } -static int fat_get_block(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create) +static int __fat_get_blocks(struct inode *inode, sector_t iblock, + unsigned long *max_blocks, + struct buffer_head *bh_result, int create) { struct super_block *sb = inode->i_sb; + struct msdos_sb_info *sbi = MSDOS_SB(sb); sector_t phys; - int err; + unsigned long mapped_blocks; + int err, offset; - err = fat_bmap(inode, iblock, &phys); + err = fat_bmap(inode, iblock, &phys, &mapped_blocks); if (err) return err; if (phys) { map_bh(bh_result, sb, phys); + *max_blocks = min(mapped_blocks, *max_blocks); return 0; } if (!create) return 0; + if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) { fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)", MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private); return -EIO; } - if (!((unsigned long)iblock & (MSDOS_SB(sb)->sec_per_clus - 1))) { + + offset = (unsigned long)iblock & (sbi->sec_per_clus - 1); + if (!offset) { + /* TODO: multiple cluster allocation would be desirable. */ err = fat_add_cluster(inode); if (err) return err; } - MSDOS_I(inode)->mmu_private += sb->s_blocksize; - err = fat_bmap(inode, iblock, &phys); + /* available blocks on this cluster */ + mapped_blocks = sbi->sec_per_clus - offset; + + *max_blocks = min(mapped_blocks, *max_blocks); + MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits; + + err = fat_bmap(inode, iblock, &phys, &mapped_blocks); if (err) return err; - if (!phys) - BUG(); + BUG_ON(!phys); + BUG_ON(*max_blocks != mapped_blocks); set_buffer_new(bh_result); map_bh(bh_result, sb, phys); return 0; } +static int fat_get_blocks(struct inode *inode, sector_t iblock, + unsigned long max_blocks, + struct buffer_head *bh_result, int create) +{ + struct super_block *sb = inode->i_sb; + int err; + + err = __fat_get_blocks(inode, iblock, &max_blocks, bh_result, create); + if (err) + return err; + bh_result->b_size = max_blocks << sb->s_blocksize_bits; + return 0; +} + +static int fat_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + unsigned long max_blocks = 1; + return __fat_get_blocks(inode, iblock, &max_blocks, bh_result, create); +} + static int fat_writepage(struct page *page, struct writeback_control *wbc) { return block_write_full_page(page, fat_get_block, wbc); @@ -128,6 +163,34 @@ static int fat_commit_write(struct file *file, struct page *page, return err; } +static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, + const struct iovec *iov, + loff_t offset, unsigned long nr_segs) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file->f_mapping->host; + + if (rw == WRITE) { + /* + * FIXME: blockdev_direct_IO() doesn't use ->prepare_write(), + * so we need to update the ->mmu_private to block boundary. + * + * But we must fill the remaining area or hole by nul for + * updating ->mmu_private. + */ + loff_t size = offset + iov_length(iov, nr_segs); + if (MSDOS_I(inode)->mmu_private < size) + return -EINVAL; + } + + /* + * FAT need to use the DIO_LOCKING for avoiding the race + * condition of fat_get_block() and ->truncate(). + */ + return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + offset, nr_segs, fat_get_blocks, NULL); +} + static sector_t _fat_bmap(struct address_space *mapping, sector_t block) { return generic_block_bmap(mapping, block, fat_get_block); @@ -141,6 +204,7 @@ static struct address_space_operations fat_aops = { .sync_page = block_sync_page, .prepare_write = fat_prepare_write, .commit_write = fat_commit_write, + .direct_IO = fat_direct_IO, .bmap = _fat_bmap }; -- cgit From 05eb0b51fb46430050d5873458612f53e0234f2e Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Sun, 8 Jan 2006 01:02:13 -0800 Subject: [PATCH] fat: support a truncate() for expanding size (generic_cont_expand) This patch changes generic_cont_expand(), in order to share the code with fatfs. - Use vmtruncate() if ->prepare_write() returns a error. Even if ->prepare_write() returns an error, it may already have added some blocks. So, this truncates blocks outside of ->i_size by vmtruncate(). - Add generic_cont_expand_simple(). The generic_cont_expand_simple() assumes that ->prepare_write() can handle the block boundary. With this, we don't need to care the extra byte. And for expanding a file size by truncate(), fatfs uses the added generic_cont_expand_simple(). Signed-off-by: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/buffer.c | 60 +++++++++++++++++++++++++++++++++++++++++++++-------------- fs/fat/file.c | 31 +++++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/buffer.c b/fs/buffer.c index 5287be18633..55023231e46 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2160,11 +2160,12 @@ int block_read_full_page(struct page *page, get_block_t *get_block) * truncates. Uses prepare/commit_write to allow the filesystem to * deal with the hole. */ -int generic_cont_expand(struct inode *inode, loff_t size) +static int __generic_cont_expand(struct inode *inode, loff_t size, + pgoff_t index, unsigned int offset) { struct address_space *mapping = inode->i_mapping; struct page *page; - unsigned long index, offset, limit; + unsigned long limit; int err; err = -EFBIG; @@ -2176,24 +2177,24 @@ int generic_cont_expand(struct inode *inode, loff_t size) if (size > inode->i_sb->s_maxbytes) goto out; - offset = (size & (PAGE_CACHE_SIZE-1)); /* Within page */ - - /* ugh. in prepare/commit_write, if from==to==start of block, we - ** skip the prepare. make sure we never send an offset for the start - ** of a block - */ - if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) { - offset++; - } - index = size >> PAGE_CACHE_SHIFT; err = -ENOMEM; page = grab_cache_page(mapping, index); if (!page) goto out; err = mapping->a_ops->prepare_write(NULL, page, offset, offset); - if (!err) { - err = mapping->a_ops->commit_write(NULL, page, offset, offset); + if (err) { + /* + * ->prepare_write() may have instantiated a few blocks + * outside i_size. Trim these off again. + */ + unlock_page(page); + page_cache_release(page); + vmtruncate(inode, inode->i_size); + goto out; } + + err = mapping->a_ops->commit_write(NULL, page, offset, offset); + unlock_page(page); page_cache_release(page); if (err > 0) @@ -2202,6 +2203,36 @@ out: return err; } +int generic_cont_expand(struct inode *inode, loff_t size) +{ + pgoff_t index; + unsigned int offset; + + offset = (size & (PAGE_CACHE_SIZE - 1)); /* Within page */ + + /* ugh. in prepare/commit_write, if from==to==start of block, we + ** skip the prepare. make sure we never send an offset for the start + ** of a block + */ + if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) { + /* caller must handle this extra byte. */ + offset++; + } + index = size >> PAGE_CACHE_SHIFT; + + return __generic_cont_expand(inode, size, index, offset); +} + +int generic_cont_expand_simple(struct inode *inode, loff_t size) +{ + loff_t pos = size - 1; + pgoff_t index = pos >> PAGE_CACHE_SHIFT; + unsigned int offset = (pos & (PAGE_CACHE_SIZE - 1)) + 1; + + /* prepare/commit_write can handle even if from==to==start of block. */ + return __generic_cont_expand(inode, size, index, offset); +} + /* * For moronic filesystems that do not allow holes in file. * We may have to extend the file. @@ -3145,6 +3176,7 @@ EXPORT_SYMBOL(fsync_bdev); EXPORT_SYMBOL(generic_block_bmap); EXPORT_SYMBOL(generic_commit_write); EXPORT_SYMBOL(generic_cont_expand); +EXPORT_SYMBOL(generic_cont_expand_simple); EXPORT_SYMBOL(init_buffer); EXPORT_SYMBOL(invalidate_bdev); EXPORT_SYMBOL(ll_rw_block); diff --git a/fs/fat/file.c b/fs/fat/file.c index 15229fe569c..9b07c328a6f 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -11,6 +11,7 @@ #include #include #include +#include int fat_generic_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) @@ -124,6 +125,24 @@ struct file_operations fat_file_operations = { .sendfile = generic_file_sendfile, }; +static int fat_cont_expand(struct inode *inode, loff_t size) +{ + struct address_space *mapping = inode->i_mapping; + loff_t start = inode->i_size, count = size - inode->i_size; + int err; + + err = generic_cont_expand_simple(inode, size); + if (err) + goto out; + + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); + if (IS_SYNC(inode)) + err = sync_page_range_nolock(inode, mapping, start, count); +out: + return err; +} + int fat_notify_change(struct dentry *dentry, struct iattr *attr) { struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); @@ -132,11 +151,17 @@ int fat_notify_change(struct dentry *dentry, struct iattr *attr) lock_kernel(); - /* FAT cannot truncate to a longer file */ + /* + * Expand the file. Since inode_setattr() updates ->i_size + * before calling the ->truncate(), but FAT needs to fill the + * hole before it. + */ if (attr->ia_valid & ATTR_SIZE) { if (attr->ia_size > inode->i_size) { - error = -EPERM; - goto out; + error = fat_cont_expand(inode, attr->ia_size); + if (error || attr->ia_valid == ATTR_SIZE) + goto out; + attr->ia_valid &= ~ATTR_SIZE; } } -- cgit From 28fd129827b00e12829d48a5290f46277600619b Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Sun, 8 Jan 2006 01:02:14 -0800 Subject: [PATCH] Fix and add EXPORT_SYMBOL(filemap_write_and_wait) This patch add EXPORT_SYMBOL(filemap_write_and_wait) and use it. See mm/filemap.c: And changes the filemap_write_and_wait() and filemap_write_and_wait_range(). Current filemap_write_and_wait() doesn't wait if filemap_fdatawrite() returns error. However, even if filemap_fdatawrite() returned an error, it may have submitted the partially data pages to the device. (e.g. in the case of -ENOSPC) Andrew Morton writes, If filemap_fdatawrite() returns an error, this might be due to some I/O problem: dead disk, unplugged cable, etc. Given the generally crappy quality of the kernel's handling of such exceptions, there's a good chance that the filemap_fdatawait() will get stuck in D state forever. So, this patch doesn't wait if filemap_fdatawrite() returns the -EIO. Trond, could you please review the nfs part? Especially I'm not sure, nfs must use the "filemap_fdatawrite(inode->i_mapping) == 0", or not. Acked-by: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/9p/vfs_dir.c | 3 +-- fs/9p/vfs_file.c | 3 +-- fs/buffer.c | 10 ++-------- fs/cifs/file.c | 6 ++---- fs/cifs/inode.c | 3 +-- fs/jfs/jfs_dmap.c | 3 +-- fs/jfs/jfs_imap.c | 6 ++---- fs/jfs/jfs_txnmgr.c | 6 ++---- fs/jfs/jfs_umount.c | 6 ++---- fs/jfs/resize.c | 3 +-- fs/jfs/super.c | 3 +-- fs/nfs/inode.c | 8 ++------ fs/smbfs/file.c | 3 +-- fs/smbfs/inode.c | 3 +-- fs/xfs/linux-2.6/xfs_fs_subr.c | 3 +-- 15 files changed, 21 insertions(+), 48 deletions(-) (limited to 'fs') diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index 57a43b8feef..17089d1905f 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -193,8 +193,7 @@ int v9fs_dir_release(struct inode *inode, struct file *filp) fid->fid); fidnum = fid->fid; - filemap_fdatawrite(inode->i_mapping); - filemap_fdatawait(inode->i_mapping); + filemap_write_and_wait(inode->i_mapping); if (fidnum >= 0) { dprintk(DEBUG_VFS, "fidopen: %d v9f->fid: %d\n", fid->fidopen, diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 89c849da850..e13577da913 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -165,8 +165,7 @@ static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl) return -ENOLCK; if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) { - filemap_fdatawrite(inode->i_mapping); - filemap_fdatawait(inode->i_mapping); + filemap_write_and_wait(inode->i_mapping); invalidate_inode_pages(&inode->i_data); } diff --git a/fs/buffer.c b/fs/buffer.c index 55023231e46..263df0f192a 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -153,14 +153,8 @@ int sync_blockdev(struct block_device *bdev) { int ret = 0; - if (bdev) { - int err; - - ret = filemap_fdatawrite(bdev->bd_inode->i_mapping); - err = filemap_fdatawait(bdev->bd_inode->i_mapping); - if (!ret) - ret = err; - } + if (bdev) + ret = filemap_write_and_wait(bdev->bd_inode->i_mapping); return ret; } EXPORT_SYMBOL(sync_blockdev); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 14a1c72ced9..5ade53d7bca 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -127,8 +127,7 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, if (file->f_dentry->d_inode->i_mapping) { /* BB no need to lock inode until after invalidate since namei code should already have it locked? */ - filemap_fdatawrite(file->f_dentry->d_inode->i_mapping); - filemap_fdatawait(file->f_dentry->d_inode->i_mapping); + filemap_write_and_wait(file->f_dentry->d_inode->i_mapping); } cFYI(1, ("invalidating remote inode since open detected it " "changed")); @@ -419,8 +418,7 @@ static int cifs_reopen_file(struct inode *inode, struct file *file, pCifsInode = CIFS_I(inode); if (pCifsInode) { if (can_flush) { - filemap_fdatawrite(inode->i_mapping); - filemap_fdatawait(inode->i_mapping); + filemap_write_and_wait(inode->i_mapping); /* temporarily disable caching while we go to server to get inode info */ pCifsInode->clientCanCacheAll = FALSE; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 411c1f7f84d..9558f51bca5 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1148,8 +1148,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) /* BB check if we need to refresh inode from server now ? BB */ /* need to flush data before changing file size on server */ - filemap_fdatawrite(direntry->d_inode->i_mapping); - filemap_fdatawait(direntry->d_inode->i_mapping); + filemap_write_and_wait(direntry->d_inode->i_mapping); if (attrs->ia_valid & ATTR_SIZE) { /* To avoid spurious oplock breaks from server, in the case of diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c index 68000a50ceb..2967b739341 100644 --- a/fs/jfs/jfs_dmap.c +++ b/fs/jfs/jfs_dmap.c @@ -302,8 +302,7 @@ int dbSync(struct inode *ipbmap) /* * write out dirty pages of bmap */ - filemap_fdatawrite(ipbmap->i_mapping); - filemap_fdatawait(ipbmap->i_mapping); + filemap_write_and_wait(ipbmap->i_mapping); diWriteSpecial(ipbmap, 0); diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c index 28201b194f5..31b4aa13dd4 100644 --- a/fs/jfs/jfs_imap.c +++ b/fs/jfs/jfs_imap.c @@ -265,8 +265,7 @@ int diSync(struct inode *ipimap) /* * write out dirty pages of imap */ - filemap_fdatawrite(ipimap->i_mapping); - filemap_fdatawait(ipimap->i_mapping); + filemap_write_and_wait(ipimap->i_mapping); diWriteSpecial(ipimap, 0); @@ -565,8 +564,7 @@ void diFreeSpecial(struct inode *ip) jfs_err("diFreeSpecial called with NULL ip!"); return; } - filemap_fdatawrite(ip->i_mapping); - filemap_fdatawait(ip->i_mapping); + filemap_write_and_wait(ip->i_mapping); truncate_inode_pages(ip->i_mapping, 0); iput(ip); } diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c index b660c93c92d..2ddb6b892bc 100644 --- a/fs/jfs/jfs_txnmgr.c +++ b/fs/jfs/jfs_txnmgr.c @@ -1231,10 +1231,8 @@ int txCommit(tid_t tid, /* transaction identifier */ * when we don't need to worry about it at all. * * if ((!S_ISDIR(ip->i_mode)) - * && (tblk->flag & COMMIT_DELETE) == 0) { - * filemap_fdatawrite(ip->i_mapping); - * filemap_fdatawait(ip->i_mapping); - * } + * && (tblk->flag & COMMIT_DELETE) == 0) + * filemap_write_and_wait(ip->i_mapping); */ /* diff --git a/fs/jfs/jfs_umount.c b/fs/jfs/jfs_umount.c index 5cf91785b54..21eaf7ac0fc 100644 --- a/fs/jfs/jfs_umount.c +++ b/fs/jfs/jfs_umount.c @@ -108,8 +108,7 @@ int jfs_umount(struct super_block *sb) * Make sure all metadata makes it to disk before we mark * the superblock as clean */ - filemap_fdatawrite(sbi->direct_inode->i_mapping); - filemap_fdatawait(sbi->direct_inode->i_mapping); + filemap_write_and_wait(sbi->direct_inode->i_mapping); /* * ensure all file system file pages are propagated to their @@ -161,8 +160,7 @@ int jfs_umount_rw(struct super_block *sb) * mark the superblock clean before everything is flushed to * disk. */ - filemap_fdatawrite(sbi->direct_inode->i_mapping); - filemap_fdatawait(sbi->direct_inode->i_mapping); + filemap_write_and_wait(sbi->direct_inode->i_mapping); updateSuper(sb, FM_CLEAN); diff --git a/fs/jfs/resize.c b/fs/jfs/resize.c index c6dc254d325..45180361871 100644 --- a/fs/jfs/resize.c +++ b/fs/jfs/resize.c @@ -376,8 +376,7 @@ int jfs_extendfs(struct super_block *sb, s64 newLVSize, int newLogSize) * by txCommit(); */ filemap_fdatawait(ipbmap->i_mapping); - filemap_fdatawrite(ipbmap->i_mapping); - filemap_fdatawait(ipbmap->i_mapping); + filemap_write_and_wait(ipbmap->i_mapping); diWriteSpecial(ipbmap, 0); newPage = nPages; /* first new page number */ diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 4226af3ea91..8d31f133643 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -502,8 +502,7 @@ out_no_rw: jfs_err("jfs_umount failed with return code %d", rc); } out_mount_failed: - filemap_fdatawrite(sbi->direct_inode->i_mapping); - filemap_fdatawait(sbi->direct_inode->i_mapping); + filemap_write_and_wait(sbi->direct_inode->i_mapping); truncate_inode_pages(sbi->direct_inode->i_mapping, 0); make_bad_inode(sbi->direct_inode); iput(sbi->direct_inode); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index e7bd0d92600..3e4ba9cb7f8 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -644,10 +644,7 @@ int nfs_sync_mapping(struct address_space *mapping) if (mapping->nrpages == 0) return 0; unmap_mapping_range(mapping, 0, 0, 0); - ret = filemap_fdatawrite(mapping); - if (ret != 0) - goto out; - ret = filemap_fdatawait(mapping); + ret = filemap_write_and_wait(mapping); if (ret != 0) goto out; ret = nfs_wb_all(mapping->host); @@ -864,8 +861,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) nfs_begin_data_update(inode); /* Write all dirty data if we're changing file permissions or size */ if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE)) != 0) { - if (filemap_fdatawrite(inode->i_mapping) == 0) - filemap_fdatawait(inode->i_mapping); + filemap_write_and_wait(inode->i_mapping); nfs_wb_all(inode); } /* diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index b4fcfa8b55a..3c6eb3ba718 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -374,8 +374,7 @@ smb_file_release(struct inode *inode, struct file * file) /* We must flush any dirty pages now as we won't be able to write anything after close. mmap can trigger this. "openers" should perhaps include mmap'ers ... */ - filemap_fdatawrite(inode->i_mapping); - filemap_fdatawait(inode->i_mapping); + filemap_write_and_wait(inode->i_mapping); smb_close(inode); } unlock_kernel(); diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 10b994428fe..6ec88bf59b2 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -697,8 +697,7 @@ smb_notify_change(struct dentry *dentry, struct iattr *attr) DENTRY_PATH(dentry), (long) inode->i_size, (long) attr->ia_size); - filemap_fdatawrite(inode->i_mapping); - filemap_fdatawait(inode->i_mapping); + filemap_write_and_wait(inode->i_mapping); error = smb_open(dentry, O_WRONLY); if (error) diff --git a/fs/xfs/linux-2.6/xfs_fs_subr.c b/fs/xfs/linux-2.6/xfs_fs_subr.c index f89340c61bf..4fa4b1a5187 100644 --- a/fs/xfs/linux-2.6/xfs_fs_subr.c +++ b/fs/xfs/linux-2.6/xfs_fs_subr.c @@ -79,8 +79,7 @@ fs_flushinval_pages( struct inode *ip = LINVFS_GET_IP(vp); if (VN_CACHED(vp)) { - filemap_fdatawrite(ip->i_mapping); - filemap_fdatawait(ip->i_mapping); + filemap_write_and_wait(ip->i_mapping); truncate_inode_pages(ip->i_mapping, first); } -- cgit From 095975da26dba21698582e91e96be10f7417333f Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Sun, 8 Jan 2006 01:02:19 -0800 Subject: [PATCH] rcu file: use atomic primitives Use atomic_inc_not_zero for rcu files instead of special case rcuref. Signed-off-by: Nick Piggin Cc: "Paul E. McKenney" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/aio.c | 3 +-- fs/file_table.c | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/aio.c b/fs/aio.c index 5a28b69ad22..aec2b1916d1 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include @@ -514,7 +513,7 @@ static int __aio_put_req(struct kioctx *ctx, struct kiocb *req) /* Must be done under the lock to serialise against cancellation. * Call this aio_fput as it duplicates fput via the fput_work. */ - if (unlikely(rcuref_dec_and_test(&req->ki_filp->f_count))) { + if (unlikely(atomic_dec_and_test(&req->ki_filp->f_count))) { get_ioctx(ctx); spin_lock(&fput_lock); list_add(&req->ki_list, &fput_head); diff --git a/fs/file_table.c b/fs/file_table.c index c3a5e2fd663..6142250104a 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -117,7 +117,7 @@ EXPORT_SYMBOL(get_empty_filp); void fastcall fput(struct file *file) { - if (rcuref_dec_and_test(&file->f_count)) + if (atomic_dec_and_test(&file->f_count)) __fput(file); } @@ -166,7 +166,7 @@ struct file fastcall *fget(unsigned int fd) rcu_read_lock(); file = fcheck_files(files, fd); if (file) { - if (!rcuref_inc_lf(&file->f_count)) { + if (!atomic_inc_not_zero(&file->f_count)) { /* File object ref couldn't be taken */ rcu_read_unlock(); return NULL; @@ -198,7 +198,7 @@ struct file fastcall *fget_light(unsigned int fd, int *fput_needed) rcu_read_lock(); file = fcheck_files(files, fd); if (file) { - if (rcuref_inc_lf(&file->f_count)) + if (atomic_inc_not_zero(&file->f_count)) *fput_needed = 1; else /* Didn't get the reference, someone's freed */ @@ -213,7 +213,7 @@ struct file fastcall *fget_light(unsigned int fd, int *fput_needed) void put_filp(struct file *file) { - if (rcuref_dec_and_test(&file->f_count)) { + if (atomic_dec_and_test(&file->f_count)) { security_file_free(file); file_kill(file); file_free(file); -- cgit From 6625b861f8f0e429902b8671b3e70792cd99074e Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 8 Jan 2006 01:02:23 -0800 Subject: [PATCH] relayfs: decouple buffer creation from inode creation The patch series implementa or fixes 3 things that were specifically requested or suggested by relayfs users: - support for non-relay files (patches 1-6) Currently, the relayfs API only supports the creation of directories (relayfs_create_dir()) and relay files (relay_open()). These patches adds support for non-relay files (relayfs_create_file()). This is so relayfs applications can create 'control files' in relayfs itself rather than in /proc or via a netlink channel, as is currently done in the relay-app examples. Basically what this amounts to is exporting relayfs_create_file() with an additional file_ops param that clients can use to supply file operations for their own special-purpose files in relayfs. - make exported relay file ops useful (patches 7-8) The relayfs relay_file_operations have always been exported, the intent being to make it possible to create relay files in other filesystems such as debugfs. The problem, though, is that currently the file operations are too tightly coupled to relayfs to actually be used for this purpose. This patch fixes that by adding a couple of callback functions that allow a client to hook into relay_open()/close() and supply the files that will be used to represent the channel buffers; the default implementation if no callbacks are defined is to create the files in relayfs. - add an option to create global relay buffer (patches 9-10) The file creation callback also supplies an optional param, is_global, that can be used by clients to create a single global relayfs buffer instead of the default per-cpu buffers. This was suggested as being useful for certain debugging applications where it's more convenient to be able to get all the data from a single channel without having to go to the bother of dealing with per-cpu files. - cleanup, some renaming and Documentation updates (patches 11-12) There were several comments that the use of netlink in the example code was non-intuitive and in fact the whole relay-app business was needlessly confusing. Based on that feedback, the example code has been completely converted over to relayfs control files as supported by this patch, and have also been made completely self-contained. The converted examples along with a couple of new examples that demonstrate using exported relay files can be found in relay-apps tarball: http://prdownloads.sourceforge.net/relayfs/relay-apps-0.9.tar.gz?download This patch: Separate buffer create/destroy from inode create/destroy. We want to be able to associate other data and not just relay buffers with inodes. Buffer create/destroy is moved out of inode.c and into relayfs core code. Signed-off-by: Tom Zanussi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/relayfs/buffers.c | 1 + fs/relayfs/inode.c | 31 +++++++++---------------------- fs/relayfs/relay.c | 11 ++++++++--- fs/relayfs/relay.h | 2 +- 4 files changed, 19 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/relayfs/buffers.c b/fs/relayfs/buffers.c index 84e21ffa5ca..667b529944c 100644 --- a/fs/relayfs/buffers.c +++ b/fs/relayfs/buffers.c @@ -186,4 +186,5 @@ void relay_remove_buf(struct kref *kref) { struct rchan_buf *buf = container_of(kref, struct rchan_buf, kref); relayfs_remove(buf->dentry); + relay_destroy_buf(buf); } diff --git a/fs/relayfs/inode.c b/fs/relayfs/inode.c index 0f7f88d067a..379e07cd2b3 100644 --- a/fs/relayfs/inode.c +++ b/fs/relayfs/inode.c @@ -34,23 +34,13 @@ static struct backing_dev_info relayfs_backing_dev_info = { }; static struct inode *relayfs_get_inode(struct super_block *sb, int mode, - struct rchan *chan) + void *data) { - struct rchan_buf *buf = NULL; struct inode *inode; - if (S_ISREG(mode)) { - BUG_ON(!chan); - buf = relay_create_buf(chan); - if (!buf) - return NULL; - } - inode = new_inode(sb); - if (!inode) { - relay_destroy_buf(buf); + if (!inode) return NULL; - } inode->i_mode = mode; inode->i_uid = 0; @@ -62,7 +52,7 @@ static struct inode *relayfs_get_inode(struct super_block *sb, int mode, switch (mode & S_IFMT) { case S_IFREG: inode->i_fop = &relayfs_file_operations; - RELAYFS_I(inode)->buf = buf; + RELAYFS_I(inode)->buf = data; break; case S_IFDIR: inode->i_op = &simple_dir_inode_operations; @@ -83,7 +73,7 @@ static struct inode *relayfs_get_inode(struct super_block *sb, int mode, * @name: the name of the file to create * @parent: parent directory * @mode: mode - * @chan: relay channel associated with the file + * @data: user-associated data for this file * * Returns the new dentry, NULL on failure * @@ -92,7 +82,7 @@ static struct inode *relayfs_get_inode(struct super_block *sb, int mode, static struct dentry *relayfs_create_entry(const char *name, struct dentry *parent, int mode, - struct rchan *chan) + void *data) { struct dentry *d; struct inode *inode; @@ -127,7 +117,7 @@ static struct dentry *relayfs_create_entry(const char *name, goto release_mount; } - inode = relayfs_get_inode(parent->d_inode->i_sb, mode, chan); + inode = relayfs_get_inode(parent->d_inode->i_sb, mode, data); if (!inode) { d = NULL; goto release_mount; @@ -155,20 +145,20 @@ exit: * @name: the name of the file to create * @parent: parent directory * @mode: mode, if not specied the default perms are used - * @chan: channel associated with the file + * @data: user-associated data for this file * * Returns file dentry if successful, NULL otherwise. * * The file will be created user r on behalf of current user. */ struct dentry *relayfs_create_file(const char *name, struct dentry *parent, - int mode, struct rchan *chan) + int mode, void *data) { if (!mode) mode = S_IRUSR; mode = (mode & S_IALLUGO) | S_IFREG; - return relayfs_create_entry(name, parent, mode, chan); + return relayfs_create_entry(name, parent, mode, data); } /** @@ -505,9 +495,6 @@ static struct inode *relayfs_alloc_inode(struct super_block *sb) */ static void relayfs_destroy_inode(struct inode *inode) { - if (RELAYFS_I(inode)->buf) - relay_destroy_buf(RELAYFS_I(inode)->buf); - kmem_cache_free(relayfs_inode_cachep, RELAYFS_I(inode)); } diff --git a/fs/relayfs/relay.c b/fs/relayfs/relay.c index 2a6f7f12b7f..7fbda177ad8 100644 --- a/fs/relayfs/relay.c +++ b/fs/relayfs/relay.c @@ -171,12 +171,17 @@ static struct rchan_buf *relay_open_buf(struct rchan *chan, struct rchan_buf *buf; struct dentry *dentry; + buf = relay_create_buf(chan); + if (!buf) + return NULL; + /* Create file in fs */ - dentry = relayfs_create_file(filename, parent, S_IRUSR, chan); - if (!dentry) + dentry = relayfs_create_file(filename, parent, S_IRUSR, buf); + if (!dentry) { + relay_destroy_buf(buf); return NULL; + } - buf = RELAYFS_I(dentry->d_inode)->buf; buf->dentry = dentry; __relay_reset(buf, 1); diff --git a/fs/relayfs/relay.h b/fs/relayfs/relay.h index 703503fa22b..c325bb24354 100644 --- a/fs/relayfs/relay.h +++ b/fs/relayfs/relay.h @@ -4,7 +4,7 @@ struct dentry *relayfs_create_file(const char *name, struct dentry *parent, int mode, - struct rchan *chan); + void *data); extern int relayfs_remove(struct dentry *dentry); extern int relay_buf_empty(struct rchan_buf *buf); extern void relay_destroy_channel(struct kref *kref); -- cgit From 907f2c77d1653ce235e8e1fd6ce5c46005814e78 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 8 Jan 2006 01:02:24 -0800 Subject: [PATCH] relayfs: export relayfs_create_file() with fileops param This patch adds a mandatory fileops param to relayfs_create_file() and exports that function so that clients can use it to create files defined by their own set of file operations, in relayfs. The purpose is to allow relayfs applications to create their own set of 'control' files alongside their relay files in relayfs rather than having to create them in /proc or debugfs for instance. relayfs_create_file() is also used by relay_open_buf() to create the relay files for a channel. In this case, a pointer to relayfs_file_operations is passed in, along with a pointer to the buffer associated with the file. Signed-off-by: Tom Zanussi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/relayfs/inode.c | 41 ++++++++++++++++++++++++++--------------- fs/relayfs/relay.c | 3 ++- fs/relayfs/relay.h | 4 ---- 3 files changed, 28 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/relayfs/inode.c b/fs/relayfs/inode.c index 379e07cd2b3..a5e6d4f2efb 100644 --- a/fs/relayfs/inode.c +++ b/fs/relayfs/inode.c @@ -33,7 +33,9 @@ static struct backing_dev_info relayfs_backing_dev_info = { .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, }; -static struct inode *relayfs_get_inode(struct super_block *sb, int mode, +static struct inode *relayfs_get_inode(struct super_block *sb, + int mode, + struct file_operations *fops, void *data) { struct inode *inode; @@ -51,8 +53,8 @@ static struct inode *relayfs_get_inode(struct super_block *sb, int mode, inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; switch (mode & S_IFMT) { case S_IFREG: - inode->i_fop = &relayfs_file_operations; - RELAYFS_I(inode)->buf = data; + inode->i_fop = fops; + RELAYFS_I(inode)->data = data; break; case S_IFDIR: inode->i_op = &simple_dir_inode_operations; @@ -73,6 +75,7 @@ static struct inode *relayfs_get_inode(struct super_block *sb, int mode, * @name: the name of the file to create * @parent: parent directory * @mode: mode + * @fops: file operations to use for the file * @data: user-associated data for this file * * Returns the new dentry, NULL on failure @@ -82,6 +85,7 @@ static struct inode *relayfs_get_inode(struct super_block *sb, int mode, static struct dentry *relayfs_create_entry(const char *name, struct dentry *parent, int mode, + struct file_operations *fops, void *data) { struct dentry *d; @@ -117,7 +121,7 @@ static struct dentry *relayfs_create_entry(const char *name, goto release_mount; } - inode = relayfs_get_inode(parent->d_inode->i_sb, mode, data); + inode = relayfs_get_inode(parent->d_inode->i_sb, mode, fops, data); if (!inode) { d = NULL; goto release_mount; @@ -145,20 +149,26 @@ exit: * @name: the name of the file to create * @parent: parent directory * @mode: mode, if not specied the default perms are used + * @fops: file operations to use for the file * @data: user-associated data for this file * * Returns file dentry if successful, NULL otherwise. * * The file will be created user r on behalf of current user. */ -struct dentry *relayfs_create_file(const char *name, struct dentry *parent, - int mode, void *data) +struct dentry *relayfs_create_file(const char *name, + struct dentry *parent, + int mode, + struct file_operations *fops, + void *data) { + BUG_ON(!fops); + if (!mode) mode = S_IRUSR; mode = (mode & S_IALLUGO) | S_IFREG; - return relayfs_create_entry(name, parent, mode, data); + return relayfs_create_entry(name, parent, mode, fops, data); } /** @@ -173,7 +183,7 @@ struct dentry *relayfs_create_file(const char *name, struct dentry *parent, struct dentry *relayfs_create_dir(const char *name, struct dentry *parent) { int mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; - return relayfs_create_entry(name, parent, mode, NULL); + return relayfs_create_entry(name, parent, mode, NULL, NULL); } /** @@ -234,7 +244,7 @@ int relayfs_remove_dir(struct dentry *dentry) */ static int relayfs_open(struct inode *inode, struct file *filp) { - struct rchan_buf *buf = RELAYFS_I(inode)->buf; + struct rchan_buf *buf = RELAYFS_I(inode)->data; kref_get(&buf->kref); return 0; @@ -250,7 +260,7 @@ static int relayfs_open(struct inode *inode, struct file *filp) static int relayfs_mmap(struct file *filp, struct vm_area_struct *vma) { struct inode *inode = filp->f_dentry->d_inode; - return relay_mmap_buf(RELAYFS_I(inode)->buf, vma); + return relay_mmap_buf(RELAYFS_I(inode)->data, vma); } /** @@ -264,7 +274,7 @@ static unsigned int relayfs_poll(struct file *filp, poll_table *wait) { unsigned int mask = 0; struct inode *inode = filp->f_dentry->d_inode; - struct rchan_buf *buf = RELAYFS_I(inode)->buf; + struct rchan_buf *buf = RELAYFS_I(inode)->data; if (buf->finalized) return POLLERR; @@ -288,7 +298,7 @@ static unsigned int relayfs_poll(struct file *filp, poll_table *wait) */ static int relayfs_release(struct inode *inode, struct file *filp) { - struct rchan_buf *buf = RELAYFS_I(inode)->buf; + struct rchan_buf *buf = RELAYFS_I(inode)->data; kref_put(&buf->kref, relay_remove_buf); return 0; @@ -450,7 +460,7 @@ static ssize_t relayfs_read(struct file *filp, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; - struct rchan_buf *buf = RELAYFS_I(inode)->buf; + struct rchan_buf *buf = RELAYFS_I(inode)->data; size_t read_start, avail; ssize_t ret = 0; void *from; @@ -485,7 +495,7 @@ static struct inode *relayfs_alloc_inode(struct super_block *sb) struct relayfs_inode_info *p = kmem_cache_alloc(relayfs_inode_cachep, SLAB_KERNEL); if (!p) return NULL; - p->buf = NULL; + p->data = NULL; return &p->vfs_inode; } @@ -531,7 +541,7 @@ static int relayfs_fill_super(struct super_block * sb, void * data, int silent) sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = RELAYFS_MAGIC; sb->s_op = &relayfs_ops; - inode = relayfs_get_inode(sb, mode, NULL); + inode = relayfs_get_inode(sb, mode, NULL, NULL); if (!inode) return -ENOMEM; @@ -589,6 +599,7 @@ module_exit(exit_relayfs_fs) EXPORT_SYMBOL_GPL(relayfs_file_operations); EXPORT_SYMBOL_GPL(relayfs_create_dir); EXPORT_SYMBOL_GPL(relayfs_remove_dir); +EXPORT_SYMBOL_GPL(relayfs_create_file); MODULE_AUTHOR("Tom Zanussi and Karim Yaghmour "); MODULE_DESCRIPTION("Relay Filesystem"); diff --git a/fs/relayfs/relay.c b/fs/relayfs/relay.c index 7fbda177ad8..a9cd5585c45 100644 --- a/fs/relayfs/relay.c +++ b/fs/relayfs/relay.c @@ -176,7 +176,8 @@ static struct rchan_buf *relay_open_buf(struct rchan *chan, return NULL; /* Create file in fs */ - dentry = relayfs_create_file(filename, parent, S_IRUSR, buf); + dentry = relayfs_create_file(filename, parent, S_IRUSR, + &relayfs_file_operations, buf); if (!dentry) { relay_destroy_buf(buf); return NULL; diff --git a/fs/relayfs/relay.h b/fs/relayfs/relay.h index c325bb24354..0993d3e5753 100644 --- a/fs/relayfs/relay.h +++ b/fs/relayfs/relay.h @@ -1,10 +1,6 @@ #ifndef _RELAY_H #define _RELAY_H -struct dentry *relayfs_create_file(const char *name, - struct dentry *parent, - int mode, - void *data); extern int relayfs_remove(struct dentry *dentry); extern int relay_buf_empty(struct rchan_buf *buf); extern void relay_destroy_channel(struct kref *kref); -- cgit From 7431733791feb0b19453d8047b0723c744667040 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 8 Jan 2006 01:02:25 -0800 Subject: [PATCH] relayfs: add relayfs_remove_file() This patch adds and exports relayfs_remove_file(), for API symmetry (with relayfs_create_file()). Signed-off-by: Tom Zanussi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/relayfs/inode.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'fs') diff --git a/fs/relayfs/inode.c b/fs/relayfs/inode.c index a5e6d4f2efb..b2f50655736 100644 --- a/fs/relayfs/inode.c +++ b/fs/relayfs/inode.c @@ -224,6 +224,17 @@ int relayfs_remove(struct dentry *dentry) return error; } +/** + * relayfs_remove_file - remove a file from relay filesystem + * @dentry: directory dentry + * + * Returns 0 if successful, negative otherwise. + */ +int relayfs_remove_file(struct dentry *dentry) +{ + return relayfs_remove(dentry); +} + /** * relayfs_remove_dir - remove a directory in the relay filesystem * @dentry: directory dentry @@ -600,6 +611,7 @@ EXPORT_SYMBOL_GPL(relayfs_file_operations); EXPORT_SYMBOL_GPL(relayfs_create_dir); EXPORT_SYMBOL_GPL(relayfs_remove_dir); EXPORT_SYMBOL_GPL(relayfs_create_file); +EXPORT_SYMBOL_GPL(relayfs_remove_file); MODULE_AUTHOR("Tom Zanussi and Karim Yaghmour "); MODULE_DESCRIPTION("Relay Filesystem"); -- cgit From 51008f9f95a4c3158151a75f88fb03fb0f646aba Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 8 Jan 2006 01:02:26 -0800 Subject: [PATCH] relayfs: use generic_ip for private data Use inode->u.generic_ip instead of relayfs_inode_info to store pointer to user data. Clients using relayfs_file_create() to create their own files would probably more expect their data to be stored in generic_ip; we also intend in the next set of patches to get rid of relayfs-specific stuff in the file operations, so we might as well do it here. Signed-off-by: Tom Zanussi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/relayfs/inode.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/relayfs/inode.c b/fs/relayfs/inode.c index b2f50655736..7f6d2c8e91c 100644 --- a/fs/relayfs/inode.c +++ b/fs/relayfs/inode.c @@ -54,7 +54,8 @@ static struct inode *relayfs_get_inode(struct super_block *sb, switch (mode & S_IFMT) { case S_IFREG: inode->i_fop = fops; - RELAYFS_I(inode)->data = data; + if (data) + inode->u.generic_ip = data; break; case S_IFDIR: inode->i_op = &simple_dir_inode_operations; @@ -255,8 +256,9 @@ int relayfs_remove_dir(struct dentry *dentry) */ static int relayfs_open(struct inode *inode, struct file *filp) { - struct rchan_buf *buf = RELAYFS_I(inode)->data; + struct rchan_buf *buf = inode->u.generic_ip; kref_get(&buf->kref); + filp->private_data = buf; return 0; } @@ -270,8 +272,8 @@ static int relayfs_open(struct inode *inode, struct file *filp) */ static int relayfs_mmap(struct file *filp, struct vm_area_struct *vma) { - struct inode *inode = filp->f_dentry->d_inode; - return relay_mmap_buf(RELAYFS_I(inode)->data, vma); + struct rchan_buf *buf = filp->private_data; + return relay_mmap_buf(buf, vma); } /** @@ -284,8 +286,7 @@ static int relayfs_mmap(struct file *filp, struct vm_area_struct *vma) static unsigned int relayfs_poll(struct file *filp, poll_table *wait) { unsigned int mask = 0; - struct inode *inode = filp->f_dentry->d_inode; - struct rchan_buf *buf = RELAYFS_I(inode)->data; + struct rchan_buf *buf = filp->private_data; if (buf->finalized) return POLLERR; @@ -309,7 +310,7 @@ static unsigned int relayfs_poll(struct file *filp, poll_table *wait) */ static int relayfs_release(struct inode *inode, struct file *filp) { - struct rchan_buf *buf = RELAYFS_I(inode)->data; + struct rchan_buf *buf = filp->private_data; kref_put(&buf->kref, relay_remove_buf); return 0; @@ -470,8 +471,8 @@ static ssize_t relayfs_read(struct file *filp, size_t count, loff_t *ppos) { + struct rchan_buf *buf = filp->private_data; struct inode *inode = filp->f_dentry->d_inode; - struct rchan_buf *buf = RELAYFS_I(inode)->data; size_t read_start, avail; ssize_t ret = 0; void *from; -- cgit From aaea25d7a68a7f72e167dc1698b66a15edc71883 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 8 Jan 2006 01:02:26 -0800 Subject: [PATCH] relayfs: remove unused alloc/destroy_inode() Since we're no longer using relayfs_inode_info, remove relayfs_alloc_inode() and relayfs_destroy_inode() along with the relayfs inode cache. Signed-off-by: Tom Zanussi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/relayfs/inode.c | 46 +--------------------------------------------- 1 file changed, 1 insertion(+), 45 deletions(-) (limited to 'fs') diff --git a/fs/relayfs/inode.c b/fs/relayfs/inode.c index 7f6d2c8e91c..b4c3e0466e9 100644 --- a/fs/relayfs/inode.c +++ b/fs/relayfs/inode.c @@ -26,7 +26,6 @@ static struct vfsmount * relayfs_mount; static int relayfs_mount_count; -static kmem_cache_t * relayfs_inode_cachep; static struct backing_dev_info relayfs_backing_dev_info = { .ra_pages = 0, /* No readahead */ @@ -499,34 +498,6 @@ out: return ret; } -/** - * relayfs alloc_inode() implementation - */ -static struct inode *relayfs_alloc_inode(struct super_block *sb) -{ - struct relayfs_inode_info *p = kmem_cache_alloc(relayfs_inode_cachep, SLAB_KERNEL); - if (!p) - return NULL; - p->data = NULL; - - return &p->vfs_inode; -} - -/** - * relayfs destroy_inode() implementation - */ -static void relayfs_destroy_inode(struct inode *inode) -{ - kmem_cache_free(relayfs_inode_cachep, RELAYFS_I(inode)); -} - -static void init_once(void *p, kmem_cache_t *cachep, unsigned long flags) -{ - struct relayfs_inode_info *i = p; - if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) - inode_init_once(&i->vfs_inode); -} - struct file_operations relayfs_file_operations = { .open = relayfs_open, .poll = relayfs_poll, @@ -539,8 +510,6 @@ struct file_operations relayfs_file_operations = { static struct super_operations relayfs_ops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, - .alloc_inode = relayfs_alloc_inode, - .destroy_inode = relayfs_destroy_inode, }; static int relayfs_fill_super(struct super_block * sb, void * data, int silent) @@ -584,25 +553,12 @@ static struct file_system_type relayfs_fs_type = { static int __init init_relayfs_fs(void) { - int err; - - relayfs_inode_cachep = kmem_cache_create("relayfs_inode_cache", - sizeof(struct relayfs_inode_info), 0, - 0, init_once, NULL); - if (!relayfs_inode_cachep) - return -ENOMEM; - - err = register_filesystem(&relayfs_fs_type); - if (err) - kmem_cache_destroy(relayfs_inode_cachep); - - return err; + return register_filesystem(&relayfs_fs_type); } static void __exit exit_relayfs_fs(void) { unregister_filesystem(&relayfs_fs_type); - kmem_cache_destroy(relayfs_inode_cachep); } module_init(init_relayfs_fs) -- cgit From 08c541a7ade230883c48225f4ea406a0117e7c2f Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 8 Jan 2006 01:02:28 -0800 Subject: [PATCH] relayfs: add support for relay files in other filesystems This patch adds a couple of callback functions that allow a client to hook into relay_open()/close() and supply the files that will be used to represent the channel buffers; the default implementation if no callbacks are defined is to create the files in relayfs. This is to support the creation and use of relay files in other filesystems such as debugfs, as implied by the fact that relayfs_file_operations are exported. Signed-off-by: Tom Zanussi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/relayfs/buffers.c | 2 +- fs/relayfs/relay.c | 30 ++++++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/relayfs/buffers.c b/fs/relayfs/buffers.c index 667b529944c..10187812771 100644 --- a/fs/relayfs/buffers.c +++ b/fs/relayfs/buffers.c @@ -185,6 +185,6 @@ void relay_destroy_buf(struct rchan_buf *buf) void relay_remove_buf(struct kref *kref) { struct rchan_buf *buf = container_of(kref, struct rchan_buf, kref); - relayfs_remove(buf->dentry); + buf->chan->cb->remove_buf_file(buf->dentry); relay_destroy_buf(buf); } diff --git a/fs/relayfs/relay.c b/fs/relayfs/relay.c index a9cd5585c45..b9bb5690327 100644 --- a/fs/relayfs/relay.c +++ b/fs/relayfs/relay.c @@ -80,11 +80,33 @@ static void buf_unmapped_default_callback(struct rchan_buf *buf, { } +/* + * create_buf_file_create() default callback. Creates file to represent buf. + */ +static struct dentry *create_buf_file_default_callback(const char *filename, + struct dentry *parent, + int mode, + struct rchan_buf *buf) +{ + return relayfs_create_file(filename, parent, mode, + &relayfs_file_operations, buf); +} + +/* + * remove_buf_file() default callback. Removes file representing relay buffer. + */ +static int remove_buf_file_default_callback(struct dentry *dentry) +{ + return relayfs_remove(dentry); +} + /* relay channel default callbacks */ static struct rchan_callbacks default_channel_callbacks = { .subbuf_start = subbuf_start_default_callback, .buf_mapped = buf_mapped_default_callback, .buf_unmapped = buf_unmapped_default_callback, + .create_buf_file = create_buf_file_default_callback, + .remove_buf_file = remove_buf_file_default_callback, }; /** @@ -176,8 +198,8 @@ static struct rchan_buf *relay_open_buf(struct rchan *chan, return NULL; /* Create file in fs */ - dentry = relayfs_create_file(filename, parent, S_IRUSR, - &relayfs_file_operations, buf); + dentry = chan->cb->create_buf_file(filename, parent, S_IRUSR, + buf); if (!dentry) { relay_destroy_buf(buf); return NULL; @@ -220,6 +242,10 @@ static inline void setup_callbacks(struct rchan *chan, cb->buf_mapped = buf_mapped_default_callback; if (!cb->buf_unmapped) cb->buf_unmapped = buf_unmapped_default_callback; + if (!cb->create_buf_file) + cb->create_buf_file = create_buf_file_default_callback; + if (!cb->remove_buf_file) + cb->remove_buf_file = remove_buf_file_default_callback; chan->cb = cb; } -- cgit From e6c08367b8fc6dce6dfd1106f53f6ef28215b313 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 8 Jan 2006 01:02:29 -0800 Subject: [PATCH] relayfs: add support for global relay buffers This patch adds the optional is_global outparam to the create_buf_file() callback. This can be used by clients to create a single global relayfs buffer instead of the default per-cpu buffers. This was suggested as being useful for certain debugging applications where it's more convenient to be able to get all the data from a single channel without having to go to the bother of dealing with per-cpu files. Signed-off-by: Tom Zanussi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/relayfs/relay.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/relayfs/relay.c b/fs/relayfs/relay.c index b9bb5690327..2935a6ab8ff 100644 --- a/fs/relayfs/relay.c +++ b/fs/relayfs/relay.c @@ -86,7 +86,8 @@ static void buf_unmapped_default_callback(struct rchan_buf *buf, static struct dentry *create_buf_file_default_callback(const char *filename, struct dentry *parent, int mode, - struct rchan_buf *buf) + struct rchan_buf *buf, + int *is_global) { return relayfs_create_file(filename, parent, mode, &relayfs_file_operations, buf); @@ -170,14 +171,16 @@ static inline void __relay_reset(struct rchan_buf *buf, unsigned int init) void relay_reset(struct rchan *chan) { unsigned int i; + struct rchan_buf *prev = NULL; if (!chan) return; for (i = 0; i < NR_CPUS; i++) { - if (!chan->buf[i]) - continue; + if (!chan->buf[i] || chan->buf[i] == prev) + break; __relay_reset(chan->buf[i], 0); + prev = chan->buf[i]; } } @@ -188,18 +191,22 @@ void relay_reset(struct rchan *chan) */ static struct rchan_buf *relay_open_buf(struct rchan *chan, const char *filename, - struct dentry *parent) + struct dentry *parent, + int *is_global) { struct rchan_buf *buf; struct dentry *dentry; + if (*is_global) + return chan->buf[0]; + buf = relay_create_buf(chan); if (!buf) return NULL; /* Create file in fs */ dentry = chan->cb->create_buf_file(filename, parent, S_IRUSR, - buf); + buf, is_global); if (!dentry) { relay_destroy_buf(buf); return NULL; @@ -273,6 +280,7 @@ struct rchan *relay_open(const char *base_filename, unsigned int i; struct rchan *chan; char *tmpname; + int is_global = 0; if (!base_filename) return NULL; @@ -297,7 +305,8 @@ struct rchan *relay_open(const char *base_filename, for_each_online_cpu(i) { sprintf(tmpname, "%s%d", base_filename, i); - chan->buf[i] = relay_open_buf(chan, tmpname, parent); + chan->buf[i] = relay_open_buf(chan, tmpname, parent, + &is_global); chan->buf[i]->cpu = i; if (!chan->buf[i]) goto free_bufs; @@ -311,6 +320,8 @@ free_bufs: if (!chan->buf[i]) break; relay_close_buf(chan->buf[i]); + if (is_global) + break; } kfree(tmpname); @@ -420,14 +431,16 @@ void relay_destroy_channel(struct kref *kref) void relay_close(struct rchan *chan) { unsigned int i; + struct rchan_buf *prev = NULL; if (!chan) return; for (i = 0; i < NR_CPUS; i++) { - if (!chan->buf[i]) - continue; + if (!chan->buf[i] || chan->buf[i] == prev) + break; relay_close_buf(chan->buf[i]); + prev = chan->buf[i]; } if (chan->last_toobig) @@ -447,14 +460,16 @@ void relay_close(struct rchan *chan) void relay_flush(struct rchan *chan) { unsigned int i; + struct rchan_buf *prev = NULL; if (!chan) return; for (i = 0; i < NR_CPUS; i++) { - if (!chan->buf[i]) - continue; + if (!chan->buf[i] || chan->buf[i] == prev) + break; relay_switch_subbuf(chan->buf[i], 0); + prev = chan->buf[i]; } } -- cgit From 761da5c88aca34586e5b7295bd8b9be2438906f2 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sun, 8 Jan 2006 01:02:31 -0800 Subject: [PATCH] relayfs: cleanup, change relayfs_file_* to relay_file_* This patch renames relayfs_file_operations to relay_file_operations, and the file operations themselves from relayfs_XXX to relay_file_XXX, to make it more clear that they refer to relay files. Signed-off-by: Tom Zanussi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/relayfs/inode.c | 89 ++++++++++++++++++++++++++++-------------------------- fs/relayfs/relay.c | 2 +- 2 files changed, 48 insertions(+), 43 deletions(-) (limited to 'fs') diff --git a/fs/relayfs/inode.c b/fs/relayfs/inode.c index b4c3e0466e9..7b7f2cb5f0e 100644 --- a/fs/relayfs/inode.c +++ b/fs/relayfs/inode.c @@ -247,13 +247,13 @@ int relayfs_remove_dir(struct dentry *dentry) } /** - * relayfs_open - open file op for relayfs files + * relay_file_open - open file op for relay files * @inode: the inode * @filp: the file * * Increments the channel buffer refcount. */ -static int relayfs_open(struct inode *inode, struct file *filp) +static int relay_file_open(struct inode *inode, struct file *filp) { struct rchan_buf *buf = inode->u.generic_ip; kref_get(&buf->kref); @@ -263,26 +263,26 @@ static int relayfs_open(struct inode *inode, struct file *filp) } /** - * relayfs_mmap - mmap file op for relayfs files + * relay_file_mmap - mmap file op for relay files * @filp: the file * @vma: the vma describing what to map * * Calls upon relay_mmap_buf to map the file into user space. */ -static int relayfs_mmap(struct file *filp, struct vm_area_struct *vma) +static int relay_file_mmap(struct file *filp, struct vm_area_struct *vma) { struct rchan_buf *buf = filp->private_data; return relay_mmap_buf(buf, vma); } /** - * relayfs_poll - poll file op for relayfs files + * relay_file_poll - poll file op for relay files * @filp: the file * @wait: poll table * * Poll implemention. */ -static unsigned int relayfs_poll(struct file *filp, poll_table *wait) +static unsigned int relay_file_poll(struct file *filp, poll_table *wait) { unsigned int mask = 0; struct rchan_buf *buf = filp->private_data; @@ -300,14 +300,14 @@ static unsigned int relayfs_poll(struct file *filp, poll_table *wait) } /** - * relayfs_release - release file op for relayfs files + * relay_file_release - release file op for relay files * @inode: the inode * @filp: the file * * Decrements the channel refcount, as the filesystem is * no longer using it. */ -static int relayfs_release(struct inode *inode, struct file *filp) +static int relay_file_release(struct inode *inode, struct file *filp) { struct rchan_buf *buf = filp->private_data; kref_put(&buf->kref, relay_remove_buf); @@ -316,11 +316,11 @@ static int relayfs_release(struct inode *inode, struct file *filp) } /** - * relayfs_read_consume - update the consumed count for the buffer + * relay_file_read_consume - update the consumed count for the buffer */ -static void relayfs_read_consume(struct rchan_buf *buf, - size_t read_pos, - size_t bytes_consumed) +static void relay_file_read_consume(struct rchan_buf *buf, + size_t read_pos, + size_t bytes_consumed) { size_t subbuf_size = buf->chan->subbuf_size; size_t n_subbufs = buf->chan->n_subbufs; @@ -343,9 +343,9 @@ static void relayfs_read_consume(struct rchan_buf *buf, } /** - * relayfs_read_avail - boolean, are there unconsumed bytes available? + * relay_file_read_avail - boolean, are there unconsumed bytes available? */ -static int relayfs_read_avail(struct rchan_buf *buf, size_t read_pos) +static int relay_file_read_avail(struct rchan_buf *buf, size_t read_pos) { size_t bytes_produced, bytes_consumed, write_offset; size_t subbuf_size = buf->chan->subbuf_size; @@ -376,16 +376,16 @@ static int relayfs_read_avail(struct rchan_buf *buf, size_t read_pos) if (bytes_produced == bytes_consumed) return 0; - relayfs_read_consume(buf, read_pos, 0); + relay_file_read_consume(buf, read_pos, 0); return 1; } /** - * relayfs_read_subbuf_avail - return bytes available in sub-buffer + * relay_file_read_subbuf_avail - return bytes available in sub-buffer */ -static size_t relayfs_read_subbuf_avail(size_t read_pos, - struct rchan_buf *buf) +static size_t relay_file_read_subbuf_avail(size_t read_pos, + struct rchan_buf *buf) { size_t padding, avail = 0; size_t read_subbuf, read_offset, write_subbuf, write_offset; @@ -407,14 +407,14 @@ static size_t relayfs_read_subbuf_avail(size_t read_pos, } /** - * relayfs_read_start_pos - find the first available byte to read + * relay_file_read_start_pos - find the first available byte to read * * If the read_pos is in the middle of padding, return the * position of the first actually available byte, otherwise * return the original value. */ -static size_t relayfs_read_start_pos(size_t read_pos, - struct rchan_buf *buf) +static size_t relay_file_read_start_pos(size_t read_pos, + struct rchan_buf *buf) { size_t read_subbuf, padding, padding_start, padding_end; size_t subbuf_size = buf->chan->subbuf_size; @@ -433,11 +433,11 @@ static size_t relayfs_read_start_pos(size_t read_pos, } /** - * relayfs_read_end_pos - return the new read position + * relay_file_read_end_pos - return the new read position */ -static size_t relayfs_read_end_pos(struct rchan_buf *buf, - size_t read_pos, - size_t count) +static size_t relay_file_read_end_pos(struct rchan_buf *buf, + size_t read_pos, + size_t count) { size_t read_subbuf, padding, end_pos; size_t subbuf_size = buf->chan->subbuf_size; @@ -456,7 +456,7 @@ static size_t relayfs_read_end_pos(struct rchan_buf *buf, } /** - * relayfs_read - read file op for relayfs files + * relay_file_read - read file op for relay files * @filp: the file * @buffer: the userspace buffer * @count: number of bytes to read @@ -465,10 +465,10 @@ static size_t relayfs_read_end_pos(struct rchan_buf *buf, * Reads count bytes or the number of bytes available in the * current sub-buffer being read, whichever is smaller. */ -static ssize_t relayfs_read(struct file *filp, - char __user *buffer, - size_t count, - loff_t *ppos) +static ssize_t relay_file_read(struct file *filp, + char __user *buffer, + size_t count, + loff_t *ppos) { struct rchan_buf *buf = filp->private_data; struct inode *inode = filp->f_dentry->d_inode; @@ -477,11 +477,11 @@ static ssize_t relayfs_read(struct file *filp, void *from; down(&inode->i_sem); - if(!relayfs_read_avail(buf, *ppos)) + if(!relay_file_read_avail(buf, *ppos)) goto out; - read_start = relayfs_read_start_pos(*ppos, buf); - avail = relayfs_read_subbuf_avail(read_start, buf); + read_start = relay_file_read_start_pos(*ppos, buf); + avail = relay_file_read_subbuf_avail(read_start, buf); if (!avail) goto out; @@ -491,20 +491,20 @@ static ssize_t relayfs_read(struct file *filp, ret = -EFAULT; goto out; } - relayfs_read_consume(buf, read_start, count); - *ppos = relayfs_read_end_pos(buf, read_start, count); + relay_file_read_consume(buf, read_start, count); + *ppos = relay_file_read_end_pos(buf, read_start, count); out: up(&inode->i_sem); return ret; } -struct file_operations relayfs_file_operations = { - .open = relayfs_open, - .poll = relayfs_poll, - .mmap = relayfs_mmap, - .read = relayfs_read, +struct file_operations relay_file_operations = { + .open = relay_file_open, + .poll = relay_file_poll, + .mmap = relay_file_mmap, + .read = relay_file_read, .llseek = no_llseek, - .release = relayfs_release, + .release = relay_file_release, }; static struct super_operations relayfs_ops = { @@ -558,13 +558,18 @@ static int __init init_relayfs_fs(void) static void __exit exit_relayfs_fs(void) { + + + + + unregister_filesystem(&relayfs_fs_type); } module_init(init_relayfs_fs) module_exit(exit_relayfs_fs) -EXPORT_SYMBOL_GPL(relayfs_file_operations); +EXPORT_SYMBOL_GPL(relay_file_operations); EXPORT_SYMBOL_GPL(relayfs_create_dir); EXPORT_SYMBOL_GPL(relayfs_remove_dir); EXPORT_SYMBOL_GPL(relayfs_create_file); diff --git a/fs/relayfs/relay.c b/fs/relayfs/relay.c index 2935a6ab8ff..abf3ceaace4 100644 --- a/fs/relayfs/relay.c +++ b/fs/relayfs/relay.c @@ -90,7 +90,7 @@ static struct dentry *create_buf_file_default_callback(const char *filename, int *is_global) { return relayfs_create_file(filename, parent, mode, - &relayfs_file_operations, buf); + &relay_file_operations, buf); } /* -- cgit From b8b0af24353eafadf58a0889999700e43f135aad Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 8 Jan 2006 01:02:33 -0800 Subject: [PATCH] udf: remove bogus inode == NULL check in inode_bmap inode can never be NULL when calling this function. Signed-off-by: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/udf/inode.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'fs') diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 4014f17d382..395e582ee54 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1957,11 +1957,6 @@ int8_t inode_bmap(struct inode *inode, int block, kernel_lb_addr *bloc, uint32_t printk(KERN_ERR "udf: inode_bmap: block < 0\n"); return -1; } - if (!inode) - { - printk(KERN_ERR "udf: inode_bmap: NULL inode\n"); - return -1; - } *extoffset = 0; *elen = 0; -- cgit From 4a30131e7dbb17e5fec6958bfac9da9aff1fa29b Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sun, 8 Jan 2006 01:02:39 -0800 Subject: [PATCH] Fix some problems with truncate and mtime semantics. SUS requires that when truncating a file to the size that it currently is: truncate and ftruncate should NOT modify ctime or mtime O_TRUNC SHOULD modify ctime and mtime. Currently mtime and ctime are always modified on most local filesystems (side effect of ->truncate) or never modified (on NFS). With this patch: ATTR_CTIME|ATTR_MTIME are sent with ATTR_SIZE precisely when an update of these times is required whether size changes or not (via a new argument to do_truncate). This allows NFS to do the right thing for O_TRUNC. inode_setattr nolonger forces ATTR_MTIME|ATTR_CTIME when the ATTR_SIZE sets the size to it's current value. This allows local filesystems to do the right thing for f?truncate. Also, the logic in inode_setattr is changed a bit so there are two return points. One returns the error from vmtruncate if it failed, the other returns 0 (there can be no other failure). Finally, if vmtruncate succeeds, and ATTR_SIZE is the only change requested, we now fall-through and mark_inode_dirty. If a filesystem did not have a ->truncate function, then vmtruncate will have changed i_size, without marking the inode as 'dirty', and I think this is wrong. Signed-off-by: Neil Brown Cc: Christoph Hellwig Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/attr.c | 24 ++++++++---------------- fs/exec.c | 2 +- fs/namei.c | 2 +- fs/open.c | 9 +++++---- 4 files changed, 15 insertions(+), 22 deletions(-) (limited to 'fs') diff --git a/fs/attr.c b/fs/attr.c index 67bcd9b14ea..b34732506f1 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -67,20 +67,12 @@ EXPORT_SYMBOL(inode_change_ok); int inode_setattr(struct inode * inode, struct iattr * attr) { unsigned int ia_valid = attr->ia_valid; - int error = 0; - - if (ia_valid & ATTR_SIZE) { - if (attr->ia_size != i_size_read(inode)) { - error = vmtruncate(inode, attr->ia_size); - if (error || (ia_valid == ATTR_SIZE)) - goto out; - } else { - /* - * We skipped the truncate but must still update - * timestamps - */ - ia_valid |= ATTR_MTIME|ATTR_CTIME; - } + + if (ia_valid & ATTR_SIZE && + attr->ia_size != i_size_read(inode)) { + int error = vmtruncate(inode, attr->ia_size); + if (error) + return error; } if (ia_valid & ATTR_UID) @@ -104,8 +96,8 @@ int inode_setattr(struct inode * inode, struct iattr * attr) inode->i_mode = mode; } mark_inode_dirty(inode); -out: - return error; + + return 0; } EXPORT_SYMBOL(inode_setattr); diff --git a/fs/exec.c b/fs/exec.c index e9650cd22a3..2075b674d85 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1505,7 +1505,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) goto close_fail; if (!file->f_op->write) goto close_fail; - if (do_truncate(file->f_dentry, 0, file) != 0) + if (do_truncate(file->f_dentry, 0, 0, file) != 0) goto close_fail; retval = binfmt->core_dump(signr, regs, file); diff --git a/fs/namei.c b/fs/namei.c index 6dbbd42d8b9..300eae088d5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1491,7 +1491,7 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) if (!error) { DQUOT_INIT(inode); - error = do_truncate(dentry, 0, NULL); + error = do_truncate(dentry, 0, ATTR_MTIME|ATTR_CTIME, NULL); } put_write_access(inode); if (error) diff --git a/fs/open.c b/fs/open.c index f53a5b9ffb7..94968cb3afc 100644 --- a/fs/open.c +++ b/fs/open.c @@ -194,7 +194,8 @@ out: return error; } -int do_truncate(struct dentry *dentry, loff_t length, struct file *filp) +int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, + struct file *filp) { int err; struct iattr newattrs; @@ -204,7 +205,7 @@ int do_truncate(struct dentry *dentry, loff_t length, struct file *filp) return -EINVAL; newattrs.ia_size = length; - newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; + newattrs.ia_valid = ATTR_SIZE | time_attrs; if (filp) { newattrs.ia_file = filp; newattrs.ia_valid |= ATTR_FILE; @@ -266,7 +267,7 @@ static inline long do_sys_truncate(const char __user * path, loff_t length) error = locks_verify_truncate(inode, NULL, length); if (!error) { DQUOT_INIT(inode); - error = do_truncate(nd.dentry, length, NULL); + error = do_truncate(nd.dentry, length, 0, NULL); } put_write_access(inode); @@ -318,7 +319,7 @@ static inline long do_sys_ftruncate(unsigned int fd, loff_t length, int small) error = locks_verify_truncate(inode, file, length); if (!error) - error = do_truncate(dentry, length, file); + error = do_truncate(dentry, length, 0, file); out_putf: fput(file); out: -- cgit From 2520f14ca85e38f575eed6acc6e586df246abea6 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sun, 8 Jan 2006 01:02:40 -0800 Subject: [PATCH] Fix overflow tests for compat_sys_fcntl64 locking When making an fctl locking call through compat_sys_fcntl64 (i.e. a 32bit app on a 64bit kernel), the syscall can return a locking range that is in conflict with the queried lock. If some aspect of this range does not fit in the 32bit structure, something needs to be done. The current code is wrong in several respects: - It returns data to userspace even if no conflict was found i.e. it should check l_type for F_UNLCK - It returns -EOVERFLOW too agressively. A lock range covering the last possible byte of the file (start = COMPAT_OFF_T_MAX, len = 1) should be possible, but is rejected with the current test. - A extra-long 'len' should not be a problem. If only that part of the conflicting lock that would be visible to the 32bit app needs to be reported to the 32bit app anyway. This patch addresses those three issues and adds a comment to (hopefully) record it for posterity. Note: this patch mainly affects test-cases. Real applications rarely is ever see the problems. This patch has been tested (LSB test suite), and works. Signed-off-by: Neil Brown Cc: Arnd Bergmann Cc: Christoph Hellwig Cc: Matthew Wilcox Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/compat.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/compat.c b/fs/compat.c index 55ac0324aaf..271b75d1597 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -494,9 +494,21 @@ asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd, ret = sys_fcntl(fd, cmd, (unsigned long)&f); set_fs(old_fs); if (cmd == F_GETLK && ret == 0) { - if ((f.l_start >= COMPAT_OFF_T_MAX) || - ((f.l_start + f.l_len) > COMPAT_OFF_T_MAX)) + /* GETLK was successfule and we need to return the data... + * but it needs to fit in the compat structure. + * l_start shouldn't be too big, unless the original + * start + end is greater than COMPAT_OFF_T_MAX, in which + * case the app was asking for trouble, so we return + * -EOVERFLOW in that case. + * l_len could be too big, in which case we just truncate it, + * and only allow the app to see that part of the conflicting + * lock that might make sense to it anyway + */ + + if (f.l_start > COMPAT_OFF_T_MAX) ret = -EOVERFLOW; + if (f.l_len > COMPAT_OFF_T_MAX) + f.l_len = COMPAT_OFF_T_MAX; if (ret == 0) ret = put_compat_flock(&f, compat_ptr(arg)); } @@ -515,9 +527,11 @@ asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd, (unsigned long)&f); set_fs(old_fs); if (cmd == F_GETLK64 && ret == 0) { - if ((f.l_start >= COMPAT_LOFF_T_MAX) || - ((f.l_start + f.l_len) > COMPAT_LOFF_T_MAX)) + /* need to return lock information - see above for commentary */ + if (f.l_start > COMPAT_LOFF_T_MAX) ret = -EOVERFLOW; + if (f.l_len > COMPAT_LOFF_T_MAX) + f.l_len = COMPAT_LOFF_T_MAX; if (ret == 0) ret = put_compat_flock64(&f, compat_ptr(arg)); } -- cgit From 54b21a7992a31d30c9a91f7e0a00ffdb4bd0caee Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 8 Jan 2006 01:03:05 -0800 Subject: [PATCH] fix possible PAGE_CACHE_SHIFT overflows We've had two instances recently of overflows when doing 64_bit_value = (32_bit_value << PAGE_CACHE_SHIFT) I did a tree-wide grep of `<<.*PAGE_CACHE_SHIFT' and this is the result. - afs_rxfs_fetch_descriptor.offset is of type off_t, which seems broken. - jfs and jffs are limited to 4GB anyway. - reiserfs map_block_for_writepage() takes an unsigned long for the block - it should take sector_t. (It'll fail for huge filesystems with blocksizepage_base) Cc: Oleg Drokin Cc: David Howells Cc: David Woodhouse Cc: Cc: Christoph Hellwig Cc: Anton Altaparmakov Cc: Jeff Dike Cc: Paolo 'Blaisorblade' Giarrusso Cc: Roman Zippel Cc: Cc: Miklos Szeredi Cc: Russell King Cc: Trond Myklebust Cc: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/afs/dir.c | 2 +- fs/buffer.c | 6 +++--- fs/freevxfs/vxfs_immed.c | 4 ++-- fs/jffs/inode-v23.c | 4 ++-- fs/mpage.c | 4 ++-- fs/romfs/inode.c | 6 +++--- fs/smbfs/file.c | 4 ++-- fs/sysv/dir.c | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 6682d6d7f29..5c61c24dab2 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -137,7 +137,7 @@ static inline void afs_dir_check_page(struct inode *dir, struct page *page) #endif /* determine how many magic numbers there should be in this page */ - latter = dir->i_size - (page->index << PAGE_CACHE_SHIFT); + latter = dir->i_size - page_offset(page); if (latter >= PAGE_SIZE) qty = PAGE_SIZE; else diff --git a/fs/buffer.c b/fs/buffer.c index 263df0f192a..55f0975a9b1 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1762,7 +1762,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page, * handle that here by just cleaning them. */ - block = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits); + block = (sector_t)page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits); head = page_buffers(page); bh = head; @@ -2635,7 +2635,7 @@ int block_truncate_page(struct address_space *mapping, pgoff_t index = from >> PAGE_CACHE_SHIFT; unsigned offset = from & (PAGE_CACHE_SIZE-1); unsigned blocksize; - pgoff_t iblock; + sector_t iblock; unsigned length, pos; struct inode *inode = mapping->host; struct page *page; @@ -2651,7 +2651,7 @@ int block_truncate_page(struct address_space *mapping, return 0; length = blocksize - length; - iblock = index << (PAGE_CACHE_SHIFT - inode->i_blkbits); + iblock = (sector_t)index << (PAGE_CACHE_SHIFT - inode->i_blkbits); page = grab_cache_page(mapping, index); err = -ENOMEM; diff --git a/fs/freevxfs/vxfs_immed.c b/fs/freevxfs/vxfs_immed.c index d0401dc68d4..6f5df1700e9 100644 --- a/fs/freevxfs/vxfs_immed.c +++ b/fs/freevxfs/vxfs_immed.c @@ -99,8 +99,8 @@ static int vxfs_immed_readpage(struct file *fp, struct page *pp) { struct vxfs_inode_info *vip = VXFS_INO(pp->mapping->host); - u_int64_t offset = pp->index << PAGE_CACHE_SHIFT; - caddr_t kaddr; + u_int64_t offset = (u_int64_t)pp->index << PAGE_CACHE_SHIFT; + caddr_t kaddr; kaddr = kmap(pp); memcpy(kaddr, vip->vii_immed.vi_immed + offset, PAGE_CACHE_SIZE); diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c index 3dcc6d2162c..2559ee10bed 100644 --- a/fs/jffs/inode-v23.c +++ b/fs/jffs/inode-v23.c @@ -757,7 +757,7 @@ jffs_do_readpage_nolock(struct file *file, struct page *page) read_len = 0; result = 0; - offset = page->index << PAGE_CACHE_SHIFT; + offset = page_offset(page); kmap(page); buf = page_address(page); @@ -1545,7 +1545,7 @@ jffs_commit_write(struct file *filp, struct page *page, { void *addr = page_address(page) + from; /* XXX: PAGE_CACHE_SHIFT or PAGE_SHIFT */ - loff_t pos = (page->index<index << (PAGE_CACHE_SHIFT - blkbits); + block_in_file = (sector_t)page->index << (PAGE_CACHE_SHIFT - blkbits); last_block = (i_size_read(inode) + blocksize - 1) >> blkbits; bh.b_page = page; @@ -466,7 +466,7 @@ __mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, * The page has no buffers: map it to disk */ BUG_ON(!PageUptodate(page)); - block_in_file = page->index << (PAGE_CACHE_SHIFT - blkbits); + block_in_file = (sector_t)page->index << (PAGE_CACHE_SHIFT - blkbits); last_block = (i_size - 1) >> blkbits; map_bh.b_page = page; for (page_block = 0; page_block < blocks_per_page; ) { diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index c74f382dabb..0a13859fd57 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -418,7 +418,7 @@ static int romfs_readpage(struct file *file, struct page * page) { struct inode *inode = page->mapping->host; - unsigned long offset, avail, readlen; + loff_t offset, avail, readlen; void *buf; int result = -EIO; @@ -429,8 +429,8 @@ romfs_readpage(struct file *file, struct page * page) goto err_out; /* 32 bit warning -- but not for us :) */ - offset = page->index << PAGE_CACHE_SHIFT; - if (offset < inode->i_size) { + offset = page_offset(page); + if (offset < i_size_read(inode)) { avail = inode->i_size-offset; readlen = min_t(unsigned long, avail, PAGE_SIZE); if (romfs_copyfrom(inode, buf, ROMFS_I(inode)->i_dataoffset+offset, readlen) == readlen) { diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index 3c6eb3ba718..7042e62726a 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -209,8 +209,8 @@ smb_updatepage(struct file *file, struct page *page, unsigned long offset, { struct dentry *dentry = file->f_dentry; - DEBUG1("(%s/%s %d@%ld)\n", DENTRY_PATH(dentry), - count, (page->index << PAGE_CACHE_SHIFT)+offset); + DEBUG1("(%s/%s %d@%lld)\n", DENTRY_PATH(dentry), count, + ((unsigned long long)page->index << PAGE_CACHE_SHIFT) + offset); return smb_writepage_sync(dentry->d_inode, page, offset, count); } diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index 69a085abad6..cce8b05cba5 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -103,7 +103,7 @@ static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir) offset = (char *)de - kaddr; over = filldir(dirent, name, strnlen(name,SYSV_NAMELEN), - (n<inode), DT_UNKNOWN); if (over) { @@ -115,7 +115,7 @@ static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir) } done: - filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset; + filp->f_pos = ((loff_t)n << PAGE_CACHE_SHIFT) | offset; unlock_kernel(); return 0; } -- cgit From bb6f6dbaa48c53525a7a4f9d4df719c3b0b582af Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Sun, 8 Jan 2006 01:03:13 -0800 Subject: [PATCH] do_coredump() should reset group_stop_count earlier __group_complete_signal() sets ->group_stop_count in sig_kernel_coredump() path and marks the target thread as ->group_exit_task. So any thread except group_exit_task will go to handle_group_stop()->finish_stop(). However, when group_exit_task actually starts do_coredump(), it sets SIGNAL_GROUP_EXIT, but does not reset ->group_stop_count while killing other threads. If we have not yet stopped threads in the same thread group, they all will spin in kernel mode until group_exit_task sends them SIGKILL, because ->group_stop_count > 0 means: recalc_sigpending_tsk() never clears TIF_SIGPENDING get_signal_to_deliver() goes to handle_group_stop() handle_group_stop() returns when SIGNAL_GROUP_EXIT set syscall_exit/resume_userspace notice TIF_SIGPENDING, call get_signal_to_deliver() again. So we are wasting cpu cycles, and if one of these threads is rt_task() this may be a serious problem. NOTE: do_coredump() holds ->mmap_sem, so not stopped threads can't escape coredumping after clearing ->group_stop_count. See also this thread: http://marc.theaimsgroup.com/?t=112739139900002 Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/exec.c b/fs/exec.c index 2075b674d85..fd02ea4a81e 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1462,6 +1462,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) if (!(current->signal->flags & SIGNAL_GROUP_EXIT)) { current->signal->flags = SIGNAL_GROUP_EXIT; current->signal->group_exit_code = exit_code; + current->signal->group_stop_count = 0; retval = 0; } spin_unlock_irq(¤t->sighand->siglock); @@ -1477,7 +1478,6 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) * Clear any false indication of pending signals that might * be seen by the filesystem code called to write the core file. */ - current->signal->group_stop_count = 0; clear_thread_flag(TIF_SIGPENDING); if (current->signal->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump) -- cgit From 4a0d11fae57989e24fe2ee3eff6d62d72db9716c Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sun, 8 Jan 2006 01:03:18 -0800 Subject: [PATCH] pivot_root: add comment Signed-off-by: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namespace.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs') diff --git a/fs/namespace.c b/fs/namespace.c index 2019899f2ab..e5aa1eeb574 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1526,6 +1526,10 @@ static void chroot_fs_refs(struct nameidata *old_nd, struct nameidata *new_nd) * pointed to by put_old must yield the same directory as new_root. No other * file system may be mounted on put_old. After all, new_root is a mountpoint. * + * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem. + * See Documentation/filesystems/ramfs-rootfs-initramfs.txt for alternatives + * in this situation. + * * Notes: * - we don't move root/cwd if they are not at the root (reason: if something * cared enough to change them, it's probably wrong to force them elsewhere) -- cgit From bf066c7db775a04bd761f8ea206f5522d0cf40ff Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Sun, 8 Jan 2006 01:03:19 -0800 Subject: [PATCH] shared mounts: cleanup Small cleanups in shared mounts code. Signed-off-by: Miklos Szeredi Cc: Ram Pai Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namespace.c | 2 +- fs/pnode.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/namespace.c b/fs/namespace.c index e5aa1eeb574..3e8fb61ad59 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -451,7 +451,7 @@ EXPORT_SYMBOL(may_umount); void release_mounts(struct list_head *head) { struct vfsmount *mnt; - while(!list_empty(head)) { + while (!list_empty(head)) { mnt = list_entry(head->next, struct vfsmount, mnt_hash); list_del_init(&mnt->mnt_hash); if (mnt->mnt_parent != mnt) { diff --git a/fs/pnode.c b/fs/pnode.c index aeeec8ba8dd..f1871f773f6 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -103,7 +103,7 @@ static struct vfsmount *propagation_next(struct vfsmount *m, struct vfsmount *next; struct vfsmount *master = m->mnt_master; - if ( master == origin->mnt_master ) { + if (master == origin->mnt_master) { next = next_peer(m); return ((next == origin) ? NULL : next); } else if (m->mnt_slave.next != &master->mnt_slave_list) -- cgit From 71b9625744b7d4a6a2416389a5ba464bdf11f07f Mon Sep 17 00:00:00 2001 From: Johann Lombardi Date: Sun, 8 Jan 2006 01:03:20 -0800 Subject: [PATCH] ext3: external journal device as a mount option The patch below adds a new mount option to allow the external journal device to be specified. The syntax is as follows: # mount -t ext3 -o journal_dev=0x0820 ... where 0x0820 means major=8 and minor=32. Signed-off-by: Johann Lombardi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ext3/super.c | 54 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 4e6730622d9..7c45acf9458 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -43,7 +43,8 @@ #include "acl.h" #include "namei.h" -static int ext3_load_journal(struct super_block *, struct ext3_super_block *); +static int ext3_load_journal(struct super_block *, struct ext3_super_block *, + unsigned long journal_devnum); static int ext3_create_journal(struct super_block *, struct ext3_super_block *, int); static void ext3_commit_super (struct super_block * sb, @@ -628,7 +629,7 @@ enum { Opt_nouid32, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, Opt_reservation, Opt_noreservation, Opt_noload, Opt_nobh, - Opt_commit, Opt_journal_update, Opt_journal_inum, + Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_journal_dev, Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback, Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota, @@ -666,6 +667,7 @@ static match_table_t tokens = { {Opt_commit, "commit=%u"}, {Opt_journal_update, "journal=update"}, {Opt_journal_inum, "journal=%u"}, + {Opt_journal_dev, "journal_dev=%u"}, {Opt_abort, "abort"}, {Opt_data_journal, "data=journal"}, {Opt_data_ordered, "data=ordered"}, @@ -705,8 +707,9 @@ static unsigned long get_sb_block(void **data) return sb_block; } -static int parse_options (char * options, struct super_block *sb, - unsigned long * inum, unsigned long *n_blocks_count, int is_remount) +static int parse_options (char *options, struct super_block *sb, + unsigned long *inum, unsigned long *journal_devnum, + unsigned long *n_blocks_count, int is_remount) { struct ext3_sb_info *sbi = EXT3_SB(sb); char * p; @@ -839,6 +842,16 @@ static int parse_options (char * options, struct super_block *sb, return 0; *inum = option; break; + case Opt_journal_dev: + if (is_remount) { + printk(KERN_ERR "EXT3-fs: cannot specify " + "journal on remount\n"); + return 0; + } + if (match_int(&args[0], &option)) + return 0; + *journal_devnum = option; + break; case Opt_noload: set_opt (sbi->s_mount_opt, NOLOAD); break; @@ -1331,6 +1344,7 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) unsigned long logic_sb_block; unsigned long offset = 0; unsigned long journal_inum = 0; + unsigned long journal_devnum = 0; unsigned long def_mount_opts; struct inode *root; int blocksize; @@ -1411,7 +1425,8 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) set_opt(sbi->s_mount_opt, RESERVATION); - if (!parse_options ((char *) data, sb, &journal_inum, NULL, 0)) + if (!parse_options ((char *) data, sb, &journal_inum, &journal_devnum, + NULL, 0)) goto failed_mount; sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | @@ -1622,7 +1637,7 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) */ if (!test_opt(sb, NOLOAD) && EXT3_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { - if (ext3_load_journal(sb, es)) + if (ext3_load_journal(sb, es, journal_devnum)) goto failed_mount2; } else if (journal_inum) { if (ext3_create_journal(sb, es, journal_inum)) @@ -1902,15 +1917,24 @@ out_bdev: return NULL; } -static int ext3_load_journal(struct super_block * sb, - struct ext3_super_block * es) +static int ext3_load_journal(struct super_block *sb, + struct ext3_super_block *es, + unsigned long journal_devnum) { journal_t *journal; int journal_inum = le32_to_cpu(es->s_journal_inum); - dev_t journal_dev = new_decode_dev(le32_to_cpu(es->s_journal_dev)); + dev_t journal_dev; int err = 0; int really_read_only; + if (journal_devnum && + journal_devnum != le32_to_cpu(es->s_journal_dev)) { + printk(KERN_INFO "EXT3-fs: external journal device major/minor " + "numbers have changed\n"); + journal_dev = new_decode_dev(journal_devnum); + } else + journal_dev = new_decode_dev(le32_to_cpu(es->s_journal_dev)); + really_read_only = bdev_read_only(sb->s_bdev); /* @@ -1969,6 +1993,16 @@ static int ext3_load_journal(struct super_block * sb, EXT3_SB(sb)->s_journal = journal; ext3_clear_journal_err(sb, es); + + if (journal_devnum && + journal_devnum != le32_to_cpu(es->s_journal_dev)) { + es->s_journal_dev = cpu_to_le32(journal_devnum); + sb->s_dirt = 1; + + /* Make sure we flush the recovery flag to disk. */ + ext3_commit_super(sb, es, 1); + } + return 0; } @@ -2197,7 +2231,7 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data) /* * Allow the "check" option to be passed as a remount option. */ - if (!parse_options(data, sb, NULL, &n_blocks_count, 1)) { + if (!parse_options(data, sb, NULL, NULL, &n_blocks_count, 1)) { err = -EINVAL; goto restore_opts; } -- cgit From 9f40668d7d14d4d16cedc2104bfb63a43584dacf Mon Sep 17 00:00:00 2001 From: Glauber de Oliveira Costa Date: Sun, 8 Jan 2006 01:03:22 -0800 Subject: [PATCH] ext3: remove trailing newlines from ext3_warning() calls Remove the trailing newlines in calls to ext3_warning(). This function already adds a trailing newline to the end of messages. Signed-off-by: Glauber de Oliveira Costa Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ext3/ialloc.c | 6 +++--- fs/ext3/namei.c | 2 +- fs/ext3/resize.c | 22 +++++++++++----------- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c index 9e4a2437621..69078079b19 100644 --- a/fs/ext3/ialloc.c +++ b/fs/ext3/ialloc.c @@ -651,7 +651,7 @@ struct inode *ext3_orphan_get(struct super_block *sb, unsigned long ino) /* Error cases - e2fsck has already cleaned up for us */ if (ino > max_ino) { ext3_warning(sb, __FUNCTION__, - "bad orphan ino %lu! e2fsck was run?\n", ino); + "bad orphan ino %lu! e2fsck was run?", ino); goto out; } @@ -660,7 +660,7 @@ struct inode *ext3_orphan_get(struct super_block *sb, unsigned long ino) bitmap_bh = read_inode_bitmap(sb, block_group); if (!bitmap_bh) { ext3_warning(sb, __FUNCTION__, - "inode bitmap error for orphan %lu\n", ino); + "inode bitmap error for orphan %lu", ino); goto out; } @@ -672,7 +672,7 @@ struct inode *ext3_orphan_get(struct super_block *sb, unsigned long ino) !(inode = iget(sb, ino)) || is_bad_inode(inode) || NEXT_ORPHAN(inode) > max_ino) { ext3_warning(sb, __FUNCTION__, - "bad orphan inode %lu! e2fsck was run?\n", ino); + "bad orphan inode %lu! e2fsck was run?", ino); printk(KERN_NOTICE "ext3_test_bit(bit=%d, block=%llu) = %d\n", bit, (unsigned long long)bitmap_bh->b_blocknr, ext3_test_bit(bit, bitmap_bh->b_data)); diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index b3c690a3b54..af193a304ee 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1476,7 +1476,7 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry, if (levels && (dx_get_count(frames->entries) == dx_get_limit(frames->entries))) { ext3_warning(sb, __FUNCTION__, - "Directory index full!\n"); + "Directory index full!"); err = -ENOSPC; goto cleanup; } diff --git a/fs/ext3/resize.c b/fs/ext3/resize.c index 6104ad31050..675aa24b005 100644 --- a/fs/ext3/resize.c +++ b/fs/ext3/resize.c @@ -340,7 +340,7 @@ static int verify_reserved_gdb(struct super_block *sb, while ((grp = ext3_list_backups(sb, &three, &five, &seven)) < end) { if (le32_to_cpu(*p++) != grp * EXT3_BLOCKS_PER_GROUP(sb) + blk){ ext3_warning(sb, __FUNCTION__, - "reserved GDT %ld missing grp %d (%ld)\n", + "reserved GDT %ld missing grp %d (%ld)", blk, grp, grp * EXT3_BLOCKS_PER_GROUP(sb) + blk); return -EINVAL; @@ -393,7 +393,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode, if (EXT3_SB(sb)->s_sbh->b_blocknr != le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block)) { ext3_warning(sb, __FUNCTION__, - "won't resize using backup superblock at %llu\n", + "won't resize using backup superblock at %llu", (unsigned long long)EXT3_SB(sb)->s_sbh->b_blocknr); return -EPERM; } @@ -417,7 +417,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode, data = (__u32 *)dind->b_data; if (le32_to_cpu(data[gdb_num % EXT3_ADDR_PER_BLOCK(sb)]) != gdblock) { ext3_warning(sb, __FUNCTION__, - "new group %u GDT block %lu not reserved\n", + "new group %u GDT block %lu not reserved", input->group, gdblock); err = -EINVAL; goto exit_dind; @@ -540,7 +540,7 @@ static int reserve_backup_gdb(handle_t *handle, struct inode *inode, for (res = 0; res < reserved_gdb; res++, blk++) { if (le32_to_cpu(*data) != blk) { ext3_warning(sb, __FUNCTION__, - "reserved block %lu not at offset %ld\n", + "reserved block %lu not at offset %ld", blk, (long)(data - (__u32 *)dind->b_data)); err = -EINVAL; goto exit_bh; @@ -683,7 +683,7 @@ exit_err: if (err) { ext3_warning(sb, __FUNCTION__, "can't update backup for group %d (err %d), " - "forcing fsck on next reboot\n", group, err); + "forcing fsck on next reboot", group, err); sbi->s_mount_state &= ~EXT3_VALID_FS; sbi->s_es->s_state &= ~cpu_to_le16(EXT3_VALID_FS); mark_buffer_dirty(sbi->s_sbh); @@ -722,7 +722,7 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) if (gdb_off == 0 && !EXT3_HAS_RO_COMPAT_FEATURE(sb, EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER)) { ext3_warning(sb, __FUNCTION__, - "Can't resize non-sparse filesystem further\n"); + "Can't resize non-sparse filesystem further"); return -EPERM; } @@ -730,13 +730,13 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) if (!EXT3_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_RESIZE_INODE)){ ext3_warning(sb, __FUNCTION__, - "No reserved GDT blocks, can't resize\n"); + "No reserved GDT blocks, can't resize"); return -EPERM; } inode = iget(sb, EXT3_RESIZE_INO); if (!inode || is_bad_inode(inode)) { ext3_warning(sb, __FUNCTION__, - "Error opening resize inode\n"); + "Error opening resize inode"); iput(inode); return -ENOENT; } @@ -766,7 +766,7 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) lock_super(sb); if (input->group != EXT3_SB(sb)->s_groups_count) { ext3_warning(sb, __FUNCTION__, - "multiple resizers run on filesystem!\n"); + "multiple resizers run on filesystem!"); err = -EBUSY; goto exit_journal; } @@ -937,7 +937,7 @@ int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es, if (last == 0) { ext3_warning(sb, __FUNCTION__, - "need to use ext2online to resize further\n"); + "need to use ext2online to resize further"); return -EPERM; } @@ -973,7 +973,7 @@ int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es, lock_super(sb); if (o_blocks_count != le32_to_cpu(es->s_blocks_count)) { ext3_warning(sb, __FUNCTION__, - "multiple resizers run on filesystem!\n"); + "multiple resizers run on filesystem!"); err = -EBUSY; goto exit_put; } -- cgit From 29ba17231222c42ca3df5424f43949e2a6fddec2 Mon Sep 17 00:00:00 2001 From: Glauber de Oliveira Costa Date: Sun, 8 Jan 2006 01:03:23 -0800 Subject: [PATCH] ext3: use sbi instead of EXT3_SB() in resize code. There are places in the resize code in which EXT3_SB() macro is used after an statement like sbi = EXT3_SB(sb) is done. Inside the same function, both sbi and EXT3_SB() are used to reference the super block Altough it is not wrong, keeping it coherent increases legibility, IMHO. Signed-off-by: Glauber de Oliveira Costa Cc: "Stephen C. Tweedie" Cc: Andreas Dilger Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ext3/resize.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/ext3/resize.c b/fs/ext3/resize.c index 675aa24b005..1041dab6de2 100644 --- a/fs/ext3/resize.c +++ b/fs/ext3/resize.c @@ -31,7 +31,7 @@ static int verify_group_input(struct super_block *sb, unsigned start = le32_to_cpu(es->s_blocks_count); unsigned end = start + input->blocks_count; unsigned group = input->group; - unsigned itend = input->inode_table + EXT3_SB(sb)->s_itb_per_group; + unsigned itend = input->inode_table + sbi->s_itb_per_group; unsigned overhead = ext3_bg_has_super(sb, group) ? (1 + ext3_bg_num_gdb(sb, group) + le16_to_cpu(es->s_reserved_gdt_blocks)) : 0; @@ -764,7 +764,7 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) } lock_super(sb); - if (input->group != EXT3_SB(sb)->s_groups_count) { + if (input->group != sbi->s_groups_count) { ext3_warning(sb, __FUNCTION__, "multiple resizers run on filesystem!"); err = -EBUSY; @@ -799,7 +799,7 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) * data. So we need to be careful to set all of the relevant * group descriptor data etc. *before* we enable the group. * - * The key field here is EXT3_SB(sb)->s_groups_count: as long as + * The key field here is sbi->s_groups_count: as long as * that retains its old value, nobody is going to access the new * group. * @@ -859,7 +859,7 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) smp_wmb(); /* Update the global fs size fields */ - EXT3_SB(sb)->s_groups_count++; + sbi->s_groups_count++; ext3_journal_dirty_metadata(handle, primary); @@ -874,7 +874,7 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) percpu_counter_mod(&sbi->s_freeinodes_counter, EXT3_INODES_PER_GROUP(sb)); - ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh); + ext3_journal_dirty_metadata(handle, sbi->s_sbh); sb->s_dirt = 1; exit_journal: -- cgit From 850d6fbe70c62a9792eac3e8ef34f2f09f209895 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Sun, 8 Jan 2006 01:03:29 -0800 Subject: [PATCH] sigio: cleanup, don't take tasklist twice The only user of send_sigio_to_task() already holds tasklist_lock, so it is better not to send the signal via send_group_sig_info() (which takes tasklist recursively) but use group_send_sig_info(). The same change in send_sigurg()->send_sigurg_to_task(). Signed-off-by: Oleg Nesterov Cc: "Paul E. McKenney" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fcntl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/fcntl.c b/fs/fcntl.c index 863b46e0d78..9903bde475f 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -457,11 +457,11 @@ static void send_sigio_to_task(struct task_struct *p, else si.si_band = band_table[reason - POLL_IN]; si.si_fd = fd; - if (!send_group_sig_info(fown->signum, &si, p)) + if (!group_send_sig_info(fown->signum, &si, p)) break; /* fall-through: fall back on the old plain SIGIO signal */ case 0: - send_group_sig_info(SIGIO, SEND_SIG_PRIV, p); + group_send_sig_info(SIGIO, SEND_SIG_PRIV, p); } } @@ -495,7 +495,7 @@ static void send_sigurg_to_task(struct task_struct *p, struct fown_struct *fown) { if (sigio_perm(p, fown, SIGURG)) - send_group_sig_info(SIGURG, SEND_SIG_PRIV, p); + group_send_sig_info(SIGURG, SEND_SIG_PRIV, p); } int send_sigurg(struct fown_struct *fown) -- cgit From 21b6bf143d05d77c350d9c6764ae090a877b66ea Mon Sep 17 00:00:00 2001 From: Jorn Dreyer Date: Sun, 8 Jan 2006 01:03:30 -0800 Subject: [PATCH] nfsroot: do not silently stop parsing on an unknown option It would be helpful if the kernel did not silently stop parsing nfs options, but instead warned about any he does not recognize. The attached patch adds one printk to do just that. It took me a couple of hours to find my configuration mistake. Cc: "David S. Miller" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfs/nfsroot.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 985cc53b8dd..e897e00c2c9 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -275,7 +275,9 @@ static int __init root_nfs_parse(char *name, char *buf) case Opt_noacl: nfs_data.flags |= NFS_MOUNT_NOACL; break; - default : + default: + printk(KERN_WARNING "Root-NFS: unknown " + "option: %s\n", p); return 0; } } -- cgit From 5160ee6fc891a9ca114be0e90fa6655647bb64b2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 8 Jan 2006 01:03:32 -0800 Subject: [PATCH] shrink dentry struct Some long time ago, dentry struct was carefully tuned so that on 32 bits UP, sizeof(struct dentry) was exactly 128, ie a power of 2, and a multiple of memory cache lines. Then RCU was added and dentry struct enlarged by two pointers, with nice results for SMP, but not so good on UP, because breaking the above tuning (128 + 8 = 136 bytes) This patch reverts this unwanted side effect, by using an union (d_u), where d_rcu and d_child are placed so that these two fields can share their memory needs. At the time d_free() is called (and d_rcu is really used), d_child is known to be empty and not touched by the dentry freeing. Lockless lookups only access d_name, d_parent, d_lock, d_op, d_flags (so the previous content of d_child is not needed if said dentry was unhashed but still accessed by a CPU because of RCU constraints) As dentry cache easily contains millions of entries, a size reduction is worth the extra complexity of the ugly C union. Signed-off-by: Eric Dumazet Cc: Dipankar Sarma Cc: Maneesh Soni Cc: Miklos Szeredi Cc: "Paul E. McKenney" Cc: Ian Kent Cc: Paul Jackson Cc: Al Viro Cc: Christoph Hellwig Cc: Trond Myklebust Cc: Neil Brown Cc: James Morris Cc: Stephen Smalley Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/autofs4/autofs_i.h | 2 +- fs/autofs4/expire.c | 12 ++++++------ fs/autofs4/inode.c | 4 ++-- fs/autofs4/root.c | 3 ++- fs/coda/cache.c | 2 +- fs/dcache.c | 34 +++++++++++++++++----------------- fs/libfs.c | 12 ++++++------ fs/ncpfs/dir.c | 2 +- fs/ncpfs/ncplib_kernel.h | 4 ++-- fs/smbfs/cache.c | 4 ++-- 10 files changed, 40 insertions(+), 39 deletions(-) (limited to 'fs') diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index fca83e28edc..385bed09b0d 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -209,7 +209,7 @@ static inline int simple_empty_nolock(struct dentry *dentry) struct dentry *child; int ret = 0; - list_for_each_entry(child, &dentry->d_subdirs, d_child) + list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) if (simple_positive(child)) goto out; ret = 1; diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index feb6ac427d0..dc39589df16 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -105,7 +105,7 @@ repeat: next = this_parent->d_subdirs.next; resume: while (next != &this_parent->d_subdirs) { - struct dentry *dentry = list_entry(next, struct dentry, d_child); + struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); /* Negative dentry - give up */ if (!simple_positive(dentry)) { @@ -138,7 +138,7 @@ resume: } if (this_parent != top) { - next = this_parent->d_child.next; + next = this_parent->d_u.d_child.next; this_parent = this_parent->d_parent; goto resume; } @@ -163,7 +163,7 @@ repeat: next = this_parent->d_subdirs.next; resume: while (next != &this_parent->d_subdirs) { - struct dentry *dentry = list_entry(next, struct dentry, d_child); + struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); /* Negative dentry - give up */ if (!simple_positive(dentry)) { @@ -199,7 +199,7 @@ cont: } if (this_parent != parent) { - next = this_parent->d_child.next; + next = this_parent->d_u.d_child.next; this_parent = this_parent->d_parent; goto resume; } @@ -238,7 +238,7 @@ static struct dentry *autofs4_expire(struct super_block *sb, /* On exit from the loop expire is set to a dgot dentry * to expire or it's NULL */ while ( next != &root->d_subdirs ) { - struct dentry *dentry = list_entry(next, struct dentry, d_child); + struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); /* Negative dentry - give up */ if ( !simple_positive(dentry) ) { @@ -302,7 +302,7 @@ next: expired, (int)expired->d_name.len, expired->d_name.name); spin_lock(&dcache_lock); list_del(&expired->d_parent->d_subdirs); - list_add(&expired->d_parent->d_subdirs, &expired->d_child); + list_add(&expired->d_parent->d_subdirs, &expired->d_u.d_child); spin_unlock(&dcache_lock); return expired; } diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 818b37be515..2d3082854a2 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -91,7 +91,7 @@ repeat: next = this_parent->d_subdirs.next; resume: while (next != &this_parent->d_subdirs) { - struct dentry *dentry = list_entry(next, struct dentry, d_child); + struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); /* Negative dentry - don`t care */ if (!simple_positive(dentry)) { @@ -117,7 +117,7 @@ resume: if (this_parent != sbi->root) { struct dentry *dentry = this_parent; - next = this_parent->d_child.next; + next = this_parent->d_u.d_child.next; this_parent = this_parent->d_parent; spin_unlock(&dcache_lock); DPRINTK("parent dentry %p %.*s", diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 2a771ec6695..2241405ffc4 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -143,7 +143,8 @@ static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t f } while(1) { - struct dentry *de = list_entry(list, struct dentry, d_child); + struct dentry *de = list_entry(list, + struct dentry, d_u.d_child); if (!d_unhashed(de) && de->d_inode) { spin_unlock(&dcache_lock); diff --git a/fs/coda/cache.c b/fs/coda/cache.c index 80072fd9b7f..c607d923350 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -93,7 +93,7 @@ static void coda_flag_children(struct dentry *parent, int flag) spin_lock(&dcache_lock); list_for_each(child, &parent->d_subdirs) { - de = list_entry(child, struct dentry, d_child); + de = list_entry(child, struct dentry, d_u.d_child); /* don't know what to do with negative dentries */ if ( ! de->d_inode ) continue; diff --git a/fs/dcache.c b/fs/dcache.c index 17e43913868..1536f15c4d4 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -71,7 +71,7 @@ struct dentry_stat_t dentry_stat = { static void d_callback(struct rcu_head *head) { - struct dentry * dentry = container_of(head, struct dentry, d_rcu); + struct dentry * dentry = container_of(head, struct dentry, d_u.d_rcu); if (dname_external(dentry)) kfree(dentry->d_name.name); @@ -86,7 +86,7 @@ static void d_free(struct dentry *dentry) { if (dentry->d_op && dentry->d_op->d_release) dentry->d_op->d_release(dentry); - call_rcu(&dentry->d_rcu, d_callback); + call_rcu(&dentry->d_u.d_rcu, d_callback); } /* @@ -193,7 +193,7 @@ kill_it: { list_del(&dentry->d_lru); dentry_stat.nr_unused--; } - list_del(&dentry->d_child); + list_del(&dentry->d_u.d_child); dentry_stat.nr_dentry--; /* For d_free, below */ /*drops the locks, at that point nobody can reach this dentry */ dentry_iput(dentry); @@ -367,7 +367,7 @@ static inline void prune_one_dentry(struct dentry * dentry) struct dentry * parent; __d_drop(dentry); - list_del(&dentry->d_child); + list_del(&dentry->d_u.d_child); dentry_stat.nr_dentry--; /* For d_free, below */ dentry_iput(dentry); parent = dentry->d_parent; @@ -518,7 +518,7 @@ repeat: resume: while (next != &this_parent->d_subdirs) { struct list_head *tmp = next; - struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); next = tmp->next; /* Have we found a mount point ? */ if (d_mountpoint(dentry)) @@ -532,7 +532,7 @@ resume: * All done at this level ... ascend and resume the search. */ if (this_parent != parent) { - next = this_parent->d_child.next; + next = this_parent->d_u.d_child.next; this_parent = this_parent->d_parent; goto resume; } @@ -569,7 +569,7 @@ repeat: resume: while (next != &this_parent->d_subdirs) { struct list_head *tmp = next; - struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); next = tmp->next; if (!list_empty(&dentry->d_lru)) { @@ -610,7 +610,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, found); * All done at this level ... ascend and resume the search. */ if (this_parent != parent) { - next = this_parent->d_child.next; + next = this_parent->d_u.d_child.next; this_parent = this_parent->d_parent; #ifdef DCACHE_DEBUG printk(KERN_DEBUG "select_parent: ascending to %s/%s, found=%d\n", @@ -753,12 +753,12 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name) dentry->d_parent = dget(parent); dentry->d_sb = parent->d_sb; } else { - INIT_LIST_HEAD(&dentry->d_child); + INIT_LIST_HEAD(&dentry->d_u.d_child); } spin_lock(&dcache_lock); if (parent) - list_add(&dentry->d_child, &parent->d_subdirs); + list_add(&dentry->d_u.d_child, &parent->d_subdirs); dentry_stat.nr_dentry++; spin_unlock(&dcache_lock); @@ -1310,8 +1310,8 @@ already_unhashed: /* Unhash the target: dput() will then get rid of it */ __d_drop(target); - list_del(&dentry->d_child); - list_del(&target->d_child); + list_del(&dentry->d_u.d_child); + list_del(&target->d_u.d_child); /* Switch the names.. */ switch_names(dentry, target); @@ -1322,15 +1322,15 @@ already_unhashed: if (IS_ROOT(dentry)) { dentry->d_parent = target->d_parent; target->d_parent = target; - INIT_LIST_HEAD(&target->d_child); + INIT_LIST_HEAD(&target->d_u.d_child); } else { do_switch(dentry->d_parent, target->d_parent); /* And add them back to the (new) parent lists */ - list_add(&target->d_child, &target->d_parent->d_subdirs); + list_add(&target->d_u.d_child, &target->d_parent->d_subdirs); } - list_add(&dentry->d_child, &dentry->d_parent->d_subdirs); + list_add(&dentry->d_u.d_child, &dentry->d_parent->d_subdirs); spin_unlock(&target->d_lock); spin_unlock(&dentry->d_lock); write_sequnlock(&rename_lock); @@ -1568,7 +1568,7 @@ repeat: resume: while (next != &this_parent->d_subdirs) { struct list_head *tmp = next; - struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); next = tmp->next; if (d_unhashed(dentry)||!dentry->d_inode) continue; @@ -1579,7 +1579,7 @@ resume: atomic_dec(&dentry->d_count); } if (this_parent != root) { - next = this_parent->d_child.next; + next = this_parent->d_u.d_child.next; atomic_dec(&this_parent->d_count); this_parent = this_parent->d_parent; goto resume; diff --git a/fs/libfs.c b/fs/libfs.c index 58101dff2c6..9c50523382e 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -93,16 +93,16 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin) loff_t n = file->f_pos - 2; spin_lock(&dcache_lock); - list_del(&cursor->d_child); + list_del(&cursor->d_u.d_child); p = file->f_dentry->d_subdirs.next; while (n && p != &file->f_dentry->d_subdirs) { struct dentry *next; - next = list_entry(p, struct dentry, d_child); + next = list_entry(p, struct dentry, d_u.d_child); if (!d_unhashed(next) && next->d_inode) n--; p = p->next; } - list_add_tail(&cursor->d_child, p); + list_add_tail(&cursor->d_u.d_child, p); spin_unlock(&dcache_lock); } } @@ -126,7 +126,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) { struct dentry *dentry = filp->f_dentry; struct dentry *cursor = filp->private_data; - struct list_head *p, *q = &cursor->d_child; + struct list_head *p, *q = &cursor->d_u.d_child; ino_t ino; int i = filp->f_pos; @@ -153,7 +153,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) } for (p=q->next; p != &dentry->d_subdirs; p=p->next) { struct dentry *next; - next = list_entry(p, struct dentry, d_child); + next = list_entry(p, struct dentry, d_u.d_child); if (d_unhashed(next) || !next->d_inode) continue; @@ -261,7 +261,7 @@ int simple_empty(struct dentry *dentry) int ret = 0; spin_lock(&dcache_lock); - list_for_each_entry(child, &dentry->d_subdirs, d_child) + list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) if (simple_positive(child)) goto out; ret = 1; diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index a9f7a8ab1d5..cfd76f431dc 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -365,7 +365,7 @@ ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) spin_lock(&dcache_lock); next = parent->d_subdirs.next; while (next != &parent->d_subdirs) { - dent = list_entry(next, struct dentry, d_child); + dent = list_entry(next, struct dentry, d_u.d_child); if ((unsigned long)dent->d_fsdata == fpos) { if (dent->d_inode) dget_locked(dent); diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index 9e4dc30c243..799e5c2bec5 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h @@ -196,7 +196,7 @@ ncp_renew_dentries(struct dentry *parent) spin_lock(&dcache_lock); next = parent->d_subdirs.next; while (next != &parent->d_subdirs) { - dentry = list_entry(next, struct dentry, d_child); + dentry = list_entry(next, struct dentry, d_u.d_child); if (dentry->d_fsdata == NULL) ncp_age_dentry(server, dentry); @@ -218,7 +218,7 @@ ncp_invalidate_dircache_entries(struct dentry *parent) spin_lock(&dcache_lock); next = parent->d_subdirs.next; while (next != &parent->d_subdirs) { - dentry = list_entry(next, struct dentry, d_child); + dentry = list_entry(next, struct dentry, d_u.d_child); dentry->d_fsdata = NULL; ncp_age_dentry(server, dentry); next = next->next; diff --git a/fs/smbfs/cache.c b/fs/smbfs/cache.c index f3e6b81288a..74b86d9725a 100644 --- a/fs/smbfs/cache.c +++ b/fs/smbfs/cache.c @@ -66,7 +66,7 @@ smb_invalidate_dircache_entries(struct dentry *parent) spin_lock(&dcache_lock); next = parent->d_subdirs.next; while (next != &parent->d_subdirs) { - dentry = list_entry(next, struct dentry, d_child); + dentry = list_entry(next, struct dentry, d_u.d_child); dentry->d_fsdata = NULL; smb_age_dentry(server, dentry); next = next->next; @@ -100,7 +100,7 @@ smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) spin_lock(&dcache_lock); next = parent->d_subdirs.next; while (next != &parent->d_subdirs) { - dent = list_entry(next, struct dentry, d_child); + dent = list_entry(next, struct dentry, d_u.d_child); if ((unsigned long)dent->d_fsdata == fpos) { if (dent->d_inode) dget_locked(dent); -- cgit From dda6ebde96044e9b5f1b14588659b39b4e6c08e7 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Sun, 8 Jan 2006 01:03:35 -0800 Subject: [PATCH] Fix handling of ELF segments with zero filesize mmap() returns -EINVAL if given a zero length, and thus elf_map() in binfmt_elf.c does likewise if it attempts to map a (page-aligned) ELF segment with zero filesize. Such a situation never arises with the default linker scripts, but there's nothing inherently wrong with zero-filesize (but non-zero memsize) ELF segments. Custom linker scripts can generate them, and the kernel should be able to map them; this patch makes it so. Signed-off-by: David Gibson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/binfmt_elf.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index f36f2210204..288386b1def 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -288,11 +288,17 @@ static unsigned long elf_map(struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int type) { unsigned long map_addr; + unsigned long pageoffset = ELF_PAGEOFFSET(eppnt->p_vaddr); down_write(¤t->mm->mmap_sem); - map_addr = do_mmap(filep, ELF_PAGESTART(addr), - eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, type, - eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr)); + /* mmap() will return -EINVAL if given a zero size, but a + * segment with zero filesize is perfectly valid */ + if (eppnt->p_filesz + pageoffset) + map_addr = do_mmap(filep, ELF_PAGESTART(addr), + eppnt->p_filesz + pageoffset, prot, type, + eppnt->p_offset - pageoffset); + else + map_addr = ELF_PAGESTART(addr); up_write(¤t->mm->mmap_sem); return(map_addr); } -- cgit From e78c9a004aadebe22306c81d1a7f1d1278dc37f9 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Sun, 8 Jan 2006 01:03:39 -0800 Subject: [PATCH] fs: remove s_old_blocksize from struct super_block This patch inlines the single user of struct super_block field s_old_blocksize and removes the field. Signed-off-by: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/super.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/super.c b/fs/super.c index 5a347a4f673..0a30e51692c 100644 --- a/fs/super.c +++ b/fs/super.c @@ -700,8 +700,7 @@ struct super_block *get_sb_bdev(struct file_system_type *fs_type, s->s_flags = flags; strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id)); - s->s_old_blocksize = block_size(bdev); - sb_set_blocksize(s, s->s_old_blocksize); + sb_set_blocksize(s, block_size(bdev)); error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); if (error) { up_write(&s->s_umount); -- cgit From ddc0f846aa7621940b74cee0c91cd26405058a4d Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sun, 8 Jan 2006 01:04:01 -0800 Subject: [PATCH] fs/udf/balloc.c: "extern inline" -> "static inline" "extern inline" doesn't make much sense. Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/udf/balloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c index 6598a5037ac..4fae57d9d11 100644 --- a/fs/udf/balloc.c +++ b/fs/udf/balloc.c @@ -41,7 +41,7 @@ #define uint(x) xuint(x) #define xuint(x) __le ## x -extern inline int find_next_one_bit (void * addr, int size, int offset) +static inline int find_next_one_bit (void * addr, int size, int offset) { uintBPL_t * p = ((uintBPL_t *) addr) + (offset / BITS_PER_LONG); int result = offset & ~(BITS_PER_LONG-1); -- cgit From a1365647022eb05a5993f270a78e9bef3bf554eb Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 8 Jan 2006 01:04:09 -0800 Subject: [PATCH] remove gcc-2 checks Remove various things which were checking for gcc-1.x and gcc-2.x compilers. From: Adrian Bunk Some documentation updates and removes some code paths for gcc < 3.2. Acked-by: Russell King Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/cluster/masklog.h | 7 +++---- fs/xfs/xfs_log.h | 8 +------- 2 files changed, 4 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/cluster/masklog.h b/fs/ocfs2/cluster/masklog.h index f5ef5ea61a0..e8c56a3d9c6 100644 --- a/fs/ocfs2/cluster/masklog.h +++ b/fs/ocfs2/cluster/masklog.h @@ -212,11 +212,10 @@ extern struct mlog_bits mlog_and_bits, mlog_not_bits; mlog(ML_ENTRY, "ENTRY:\n"); \ } while (0) -/* We disable this for old compilers since they don't have support for - * __builtin_types_compatible_p. +/* + * We disable this for sparse. */ -#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) && \ - !defined(__CHECKER__) +#if !defined(__CHECKER__) #define mlog_exit(st) do { \ if (__builtin_types_compatible_p(typeof(st), unsigned long)) \ mlog(ML_EXIT, "EXIT: %lu\n", (unsigned long) (st)); \ diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h index 158829ca56f..f40d4391fcf 100644 --- a/fs/xfs/xfs_log.h +++ b/fs/xfs/xfs_log.h @@ -30,13 +30,7 @@ * By comparing each compnent, we don't have to worry about extra * endian issues in treating two 32 bit numbers as one 64 bit number */ -static -#if defined(__GNUC__) && (__GNUC__ == 2) && ( (__GNUC_MINOR__ == 95) || (__GNUC_MINOR__ == 96)) -__attribute__((unused)) /* gcc 2.95, 2.96 miscompile this when inlined */ -#else -__inline__ -#endif -xfs_lsn_t _lsn_cmp(xfs_lsn_t lsn1, xfs_lsn_t lsn2) +static inline xfs_lsn_t _lsn_cmp(xfs_lsn_t lsn1, xfs_lsn_t lsn2) { if (CYCLE_LSN(lsn1) != CYCLE_LSN(lsn2)) return (CYCLE_LSN(lsn1) Date: Sun, 8 Jan 2006 01:04:16 -0800 Subject: [PATCH] fs/proc/: function prototypes belong in header files Function prototypes belong into header files. Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/generic.c | 2 ++ fs/proc/inode.c | 2 +- fs/proc/internal.h | 4 ++++ fs/proc/root.c | 3 ++- 4 files changed, 9 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 72b431d0a0a..20e5c4509a4 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -21,6 +21,8 @@ #include #include +#include "internal.h" + static ssize_t proc_file_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos); static ssize_t proc_file_write(struct file *file, const char __user *buffer, diff --git a/fs/proc/inode.c b/fs/proc/inode.c index e6a818a93f3..6573f31f1fd 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -19,7 +19,7 @@ #include #include -extern void free_proc_entry(struct proc_dir_entry *); +#include "internal.h" static inline struct proc_dir_entry * de_get(struct proc_dir_entry *de) { diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 3e55198f980..95a1cf32b83 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -37,6 +37,10 @@ extern int proc_tgid_stat(struct task_struct *, char *); extern int proc_pid_status(struct task_struct *, char *); extern int proc_pid_statm(struct task_struct *, char *); +void free_proc_entry(struct proc_dir_entry *de); + +int proc_init_inodecache(void); + static inline struct task_struct *proc_task(struct inode *inode) { return PROC_I(inode)->task; diff --git a/fs/proc/root.c b/fs/proc/root.c index aef148f099a..68896283c8a 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -18,6 +18,8 @@ #include #include +#include "internal.h" + struct proc_dir_entry *proc_net, *proc_net_stat, *proc_bus, *proc_root_fs, *proc_root_driver; #ifdef CONFIG_SYSCTL @@ -36,7 +38,6 @@ static struct file_system_type proc_fs_type = { .kill_sb = kill_anon_super, }; -extern int __init proc_init_inodecache(void); void __init proc_root_init(void) { int err = proc_init_inodecache(); -- cgit From ac34dd052400b1e63aa8e711a13c0670943296fd Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 8 Jan 2006 01:04:50 -0800 Subject: [PATCH] fs/smbfs/proc.c: fix data corruption in smb_proc_setattr_unix() This patch fixes a data corruption in smb_proc_setattr_unix() (smb_filetype_from_mode() returns an u32, and there are only four bytes reserved for it in data. Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/smbfs/proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index 38ab558835c..d6baec0f24a 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -3113,7 +3113,7 @@ smb_proc_setattr_unix(struct dentry *d, struct iattr *attr, LSET(data, 32, SMB_TIME_NO_CHANGE); LSET(data, 40, SMB_UID_NO_CHANGE); LSET(data, 48, SMB_GID_NO_CHANGE); - LSET(data, 56, smb_filetype_from_mode(attr->ia_mode)); + DSET(data, 56, smb_filetype_from_mode(attr->ia_mode)); LSET(data, 60, major); LSET(data, 68, minor); LSET(data, 76, 0); -- cgit From 15b2fe3931831891a62bad9cafd403c169ae087c Mon Sep 17 00:00:00 2001 From: Evgeniy Polyakov Date: Sun, 8 Jan 2006 01:04:51 -0800 Subject: [PATCH] UFS: inode->i_sem is not released in error path Signed-off-by: Evgeniy Polyakov Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ufs/super.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 54828ebcf1b..2ba11a9aa99 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -1296,8 +1296,10 @@ static ssize_t ufs_quota_write(struct super_block *sb, int type, blk++; } out: - if (len == towrite) + if (len == towrite) { + up(&inode->i_sem); return err; + } if (inode->i_size < off+len-towrite) i_size_write(inode, off+len-towrite); inode->i_version++; -- cgit From f5ef3c105bee3a52486d7b55cef3330fcde9bca6 Mon Sep 17 00:00:00 2001 From: Eric Van Hensbergen Date: Sun, 8 Jan 2006 01:04:56 -0800 Subject: [PATCH] v9fs: fix fd_close If a 9pfs server crashes, v9fs_fd_close() is called. Subsequently, in cleaning up by performing a umount() on the FS that was provided by this server v9fs_fd_close() is called again, and uses the old, freed valus of trans->priv. This patch ensures that trans->priv can be freed only once, otherwise this function bails early. Signed-off-by: Michal Ostrowski Signed-off-by: Eric Van Hensbergen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/9p/trans_fd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/9p/trans_fd.c b/fs/9p/trans_fd.c index 63b58ce98ff..b7ffb985958 100644 --- a/fs/9p/trans_fd.c +++ b/fs/9p/trans_fd.c @@ -148,12 +148,12 @@ static void v9fs_fd_close(struct v9fs_transport *trans) if (!trans) return; - trans->status = Disconnected; - ts = trans->priv; + ts = xchg(&trans->priv, NULL); if (!ts) return; + trans->status = Disconnected; if (ts->in_file) fput(ts->in_file); -- cgit From 3cf6429a26da5c4d7b795e6d0f8f56ed2e4fdfc0 Mon Sep 17 00:00:00 2001 From: Latchesar Ionkov Date: Sun, 8 Jan 2006 01:04:58 -0800 Subject: [PATCH] v9fs: new multiplexer implementation New multiplexer implementation. Decreases the number of kernel threads required. Better handling when the user process receives a signal. Signed-off-by: Latchesar Ionkov Cc: Eric Van Hensbergen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/9p/9p.c | 68 +++- fs/9p/9p.h | 9 +- fs/9p/conv.c | 86 ++-- fs/9p/conv.h | 13 +- fs/9p/fid.c | 2 +- fs/9p/mux.c | 1122 +++++++++++++++++++++++++++++++++++++--------------- fs/9p/mux.h | 40 +- fs/9p/trans_fd.c | 49 ++- fs/9p/trans_sock.c | 161 +++++--- fs/9p/transport.h | 4 +- fs/9p/v9fs.c | 41 +- fs/9p/v9fs.h | 17 +- fs/9p/vfs_dentry.c | 13 +- fs/9p/vfs_dir.c | 17 +- fs/9p/vfs_inode.c | 89 ++--- fs/9p/vfs_super.c | 3 +- 16 files changed, 1172 insertions(+), 562 deletions(-) (limited to 'fs') diff --git a/fs/9p/9p.c b/fs/9p/9p.c index e847f504a47..a3a1ac61072 100644 --- a/fs/9p/9p.c +++ b/fs/9p/9p.c @@ -52,10 +52,11 @@ v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, dprintk(DEBUG_9P, "msize: %d version: %s\n", msize, version); msg.id = TVERSION; + msg.tag = ~0; msg.params.tversion.msize = msize; msg.params.tversion.version = version; - return v9fs_mux_rpc(v9ses, &msg, fcall); + return v9fs_mux_rpc(v9ses->mux, &msg, fcall); } /** @@ -83,7 +84,30 @@ v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname, msg.params.tattach.uname = uname; msg.params.tattach.aname = aname; - return v9fs_mux_rpc(v9ses, &msg, fcall); + return v9fs_mux_rpc(v9ses->mux, &msg, fcall); +} + +static void v9fs_t_clunk_cb(void *a, struct v9fs_fcall *tc, + struct v9fs_fcall *rc, int err) +{ + int fid; + struct v9fs_session_info *v9ses; + + if (err) + return; + + fid = tc->params.tclunk.fid; + kfree(tc); + + if (!rc) + return; + + dprintk(DEBUG_9P, "tcall id %d rcall id %d\n", tc->id, rc->id); + v9ses = a; + if (rc->id == RCLUNK) + v9fs_put_idpool(fid, &v9ses->fidpool); + + kfree(rc); } /** @@ -93,18 +117,24 @@ v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname, * @fcall: pointer to response fcall pointer * */ - int -v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_fcall **fcall) +v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid) { - struct v9fs_fcall msg; + int err; + struct v9fs_fcall *tc, *rc; + + tc = kmalloc(sizeof(struct v9fs_fcall), GFP_KERNEL); dprintk(DEBUG_9P, "fid %d\n", fid); - msg.id = TCLUNK; - msg.params.tclunk.fid = fid; + tc->id = TCLUNK; + tc->params.tclunk.fid = fid; - return v9fs_mux_rpc(v9ses, &msg, fcall); + err = v9fs_mux_rpc(v9ses->mux, tc, &rc); + if (err >= 0) { + v9fs_t_clunk_cb(v9ses, tc, rc, 0); + } + + return err; } /** @@ -121,7 +151,7 @@ int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 tag) dprintk(DEBUG_9P, "oldtag %d\n", tag); msg.id = TFLUSH; msg.params.tflush.oldtag = tag; - return v9fs_mux_rpc(v9ses, &msg, NULL); + return v9fs_mux_rpc(v9ses->mux, &msg, NULL); } /** @@ -143,7 +173,7 @@ v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, struct v9fs_fcall **fcall) msg.id = TSTAT; msg.params.tstat.fid = fid; - return v9fs_mux_rpc(v9ses, &msg, fcall); + return v9fs_mux_rpc(v9ses->mux, &msg, fcall); } /** @@ -166,7 +196,7 @@ v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid, msg.params.twstat.fid = fid; msg.params.twstat.stat = stat; - return v9fs_mux_rpc(v9ses, &msg, fcall); + return v9fs_mux_rpc(v9ses->mux, &msg, fcall); } /** @@ -199,7 +229,7 @@ v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid, msg.params.twalk.nwname = 0; } - return v9fs_mux_rpc(v9ses, &msg, fcall); + return v9fs_mux_rpc(v9ses->mux, &msg, fcall); } /** @@ -217,14 +247,14 @@ v9fs_t_open(struct v9fs_session_info *v9ses, u32 fid, u8 mode, struct v9fs_fcall **fcall) { struct v9fs_fcall msg; - long errorno = -1; + int errorno = -1; dprintk(DEBUG_9P, "fid %d mode %d\n", fid, mode); msg.id = TOPEN; msg.params.topen.fid = fid; msg.params.topen.mode = mode; - errorno = v9fs_mux_rpc(v9ses, &msg, fcall); + errorno = v9fs_mux_rpc(v9ses->mux, &msg, fcall); return errorno; } @@ -246,7 +276,7 @@ v9fs_t_remove(struct v9fs_session_info *v9ses, u32 fid, dprintk(DEBUG_9P, "fid %d\n", fid); msg.id = TREMOVE; msg.params.tremove.fid = fid; - return v9fs_mux_rpc(v9ses, &msg, fcall); + return v9fs_mux_rpc(v9ses->mux, &msg, fcall); } /** @@ -275,7 +305,7 @@ v9fs_t_create(struct v9fs_session_info *v9ses, u32 fid, char *name, msg.params.tcreate.perm = perm; msg.params.tcreate.mode = mode; - return v9fs_mux_rpc(v9ses, &msg, fcall); + return v9fs_mux_rpc(v9ses->mux, &msg, fcall); } /** @@ -302,7 +332,7 @@ v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, u64 offset, msg.params.tread.fid = fid; msg.params.tread.offset = offset; msg.params.tread.count = count; - errorno = v9fs_mux_rpc(v9ses, &msg, &rc); + errorno = v9fs_mux_rpc(v9ses->mux, &msg, &rc); if (!errorno) { errorno = rc->params.rread.count; @@ -345,7 +375,7 @@ v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, msg.params.twrite.count = count; msg.params.twrite.data = data; - errorno = v9fs_mux_rpc(v9ses, &msg, &rc); + errorno = v9fs_mux_rpc(v9ses->mux, &msg, &rc); if (!errorno) errorno = rc->params.rwrite.count; diff --git a/fs/9p/9p.h b/fs/9p/9p.h index f55424216be..6355392786e 100644 --- a/fs/9p/9p.h +++ b/fs/9p/9p.h @@ -100,6 +100,9 @@ enum { V9FS_QTFILE = 0x00, }; +#define V9FS_NOTAG (u16)(~0) +#define V9FS_NOFID (u32)(~0) + /* ample room for Twrite/Rread header (iounit) */ #define V9FS_IOHDRSZ 24 @@ -303,6 +306,9 @@ struct v9fs_fcall { } params; }; +#define V9FS_FCALLHDRSZ (sizeof(struct v9fs_fcall) + \ + sizeof(struct v9fs_stat) + 16*sizeof(struct v9fs_qid) + 16) + #define FCALL_ERROR(fcall) (fcall ? fcall->params.rerror.error : "") int v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, @@ -311,8 +317,7 @@ int v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, int v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname, u32 fid, u32 afid, struct v9fs_fcall **rcall); -int v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_fcall **rcall); +int v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid); int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 oldtag); diff --git a/fs/9p/conv.c b/fs/9p/conv.c index 18121af99d3..1b9b15dfeaf 100644 --- a/fs/9p/conv.c +++ b/fs/9p/conv.c @@ -208,7 +208,7 @@ static inline char *buf_get_stringb(struct cbuf *buf, struct cbuf *sbuf) len = buf_get_int16(buf); if (!buf_check_overflow(buf) && buf_check_size(buf, len) && - buf_check_size(sbuf, len+1)) { + buf_check_size(sbuf, len + 1)) { memcpy(sbuf->p, buf->p, len); sbuf->p[len] = 0; @@ -252,13 +252,12 @@ static inline void *buf_get_datab(struct cbuf *buf, struct cbuf *dbuf, /** * v9fs_size_stat - calculate the size of a variable length stat struct - * @v9ses: session information * @stat: metadata (stat) structure + * @extended: non-zero if 9P2000.u * */ -static int v9fs_size_stat(struct v9fs_session_info *v9ses, - struct v9fs_stat *stat) +static int v9fs_size_stat(struct v9fs_stat *stat, int extended) { int size = 0; @@ -288,7 +287,7 @@ static int v9fs_size_stat(struct v9fs_session_info *v9ses, if (stat->muid) size += strlen(stat->muid); - if (v9ses->extended) { + if (extended) { size += 4 + /* n_uid[4] */ 4 + /* n_gid[4] */ 4 + /* n_muid[4] */ @@ -302,15 +301,14 @@ static int v9fs_size_stat(struct v9fs_session_info *v9ses, /** * serialize_stat - safely format a stat structure for transmission - * @v9ses: session info * @stat: metadata (stat) structure * @bufp: buffer to serialize structure into + * @extended: non-zero if 9P2000.u * */ static int -serialize_stat(struct v9fs_session_info *v9ses, struct v9fs_stat *stat, - struct cbuf *bufp) +serialize_stat(struct v9fs_stat *stat, struct cbuf *bufp, int extended) { buf_put_int16(bufp, stat->size); buf_put_int16(bufp, stat->type); @@ -328,7 +326,7 @@ serialize_stat(struct v9fs_session_info *v9ses, struct v9fs_stat *stat, buf_put_string(bufp, stat->gid); buf_put_string(bufp, stat->muid); - if (v9ses->extended) { + if (extended) { buf_put_string(bufp, stat->extension); buf_put_int32(bufp, stat->n_uid); buf_put_int32(bufp, stat->n_gid); @@ -343,16 +341,16 @@ serialize_stat(struct v9fs_session_info *v9ses, struct v9fs_stat *stat, /** * deserialize_stat - safely decode a recieved metadata (stat) structure - * @v9ses: session info * @bufp: buffer to deserialize * @stat: metadata (stat) structure * @dbufp: buffer to deserialize variable strings into + * @extended: non-zero if 9P2000.u * */ static inline int -deserialize_stat(struct v9fs_session_info *v9ses, struct cbuf *bufp, - struct v9fs_stat *stat, struct cbuf *dbufp) +deserialize_stat(struct cbuf *bufp, struct v9fs_stat *stat, + struct cbuf *dbufp, int extended) { stat->size = buf_get_int16(bufp); @@ -370,7 +368,7 @@ deserialize_stat(struct v9fs_session_info *v9ses, struct cbuf *bufp, stat->gid = buf_get_stringb(bufp, dbufp); stat->muid = buf_get_stringb(bufp, dbufp); - if (v9ses->extended) { + if (extended) { stat->extension = buf_get_stringb(bufp, dbufp); stat->n_uid = buf_get_int32(bufp); stat->n_gid = buf_get_int32(bufp); @@ -385,20 +383,20 @@ deserialize_stat(struct v9fs_session_info *v9ses, struct cbuf *bufp, /** * deserialize_statb - wrapper for decoding a received metadata structure - * @v9ses: session info * @bufp: buffer to deserialize * @dbufp: buffer to deserialize variable strings into + * @extended: non-zero if 9P2000.u * */ -static inline struct v9fs_stat *deserialize_statb(struct v9fs_session_info - *v9ses, struct cbuf *bufp, - struct cbuf *dbufp) +static inline struct v9fs_stat *deserialize_statb(struct cbuf *bufp, + struct cbuf *dbufp, + int extended) { struct v9fs_stat *ret = buf_alloc(dbufp, sizeof(struct v9fs_stat)); if (ret) { - int n = deserialize_stat(v9ses, bufp, ret, dbufp); + int n = deserialize_stat(bufp, ret, dbufp, extended); if (n <= 0) return NULL; } @@ -408,17 +406,16 @@ static inline struct v9fs_stat *deserialize_statb(struct v9fs_session_info /** * v9fs_deserialize_stat - decode a received metadata structure - * @v9ses: session info * @buf: buffer to deserialize * @buflen: length of received buffer * @stat: metadata structure to decode into * @statlen: length of destination metadata structure + * @extended: non-zero if 9P2000.u * */ -int -v9fs_deserialize_stat(struct v9fs_session_info *v9ses, void *buf, - u32 buflen, struct v9fs_stat *stat, u32 statlen) +int v9fs_deserialize_stat(void *buf, u32 buflen, struct v9fs_stat *stat, + u32 statlen, int extended) { struct cbuf buffer; struct cbuf *bufp = &buffer; @@ -429,11 +426,10 @@ v9fs_deserialize_stat(struct v9fs_session_info *v9ses, void *buf, buf_init(dbufp, (char *)stat + sizeof(struct v9fs_stat), statlen - sizeof(struct v9fs_stat)); - return deserialize_stat(v9ses, bufp, stat, dbufp); + return deserialize_stat(bufp, stat, dbufp, extended); } -static inline int -v9fs_size_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall) +static inline int v9fs_size_fcall(struct v9fs_fcall *fcall, int extended) { int size = 4 + 1 + 2; /* size[4] msg[1] tag[2] */ int i = 0; @@ -485,7 +481,7 @@ v9fs_size_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall) break; case TWSTAT: /* fid[4] stat[n] */ fcall->params.twstat.stat->size = - v9fs_size_stat(v9ses, fcall->params.twstat.stat); + v9fs_size_stat(fcall->params.twstat.stat, extended); size += 4 + 2 + 2 + fcall->params.twstat.stat->size; } return size; @@ -493,16 +489,16 @@ v9fs_size_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall) /* * v9fs_serialize_fcall - marshall fcall struct into a packet - * @v9ses: session information * @fcall: structure to convert * @data: buffer to serialize fcall into * @datalen: length of buffer to serialize fcall into + * @extended: non-zero if 9P2000.u * */ int -v9fs_serialize_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall, - void *data, u32 datalen) +v9fs_serialize_fcall(struct v9fs_fcall *fcall, void *data, u32 datalen, + int extended) { int i = 0; struct v9fs_stat *stat = NULL; @@ -516,7 +512,7 @@ v9fs_serialize_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall, return -EINVAL; } - fcall->size = v9fs_size_fcall(v9ses, fcall); + fcall->size = v9fs_size_fcall(fcall, extended); buf_put_int32(bufp, fcall->size); buf_put_int8(bufp, fcall->id); @@ -591,31 +587,31 @@ v9fs_serialize_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall, stat = fcall->params.twstat.stat; buf_put_int16(bufp, stat->size + 2); - serialize_stat(v9ses, stat, bufp); + serialize_stat(stat, bufp, extended); break; } - if (buf_check_overflow(bufp)) + if (buf_check_overflow(bufp)) { + dprintk(DEBUG_ERROR, "buffer overflow\n"); return -EIO; + } return fcall->size; } /** * deserialize_fcall - unmarshal a response - * @v9ses: session information - * @msgsize: size of rcall message * @buf: recieved buffer * @buflen: length of received buffer * @rcall: fcall structure to populate * @rcalllen: length of fcall structure to populate + * @extended: non-zero if 9P2000.u * */ int -v9fs_deserialize_fcall(struct v9fs_session_info *v9ses, u32 msgsize, - void *buf, u32 buflen, struct v9fs_fcall *rcall, - int rcalllen) +v9fs_deserialize_fcall(void *buf, u32 buflen, struct v9fs_fcall *rcall, + int rcalllen, int extended) { struct cbuf buffer; @@ -628,7 +624,7 @@ v9fs_deserialize_fcall(struct v9fs_session_info *v9ses, u32 msgsize, buf_init(dbufp, (char *)rcall + sizeof(struct v9fs_fcall), rcalllen - sizeof(struct v9fs_fcall)); - rcall->size = msgsize; + rcall->size = buf_get_int32(bufp); rcall->id = buf_get_int8(bufp); rcall->tag = buf_get_int16(bufp); @@ -651,6 +647,12 @@ v9fs_deserialize_fcall(struct v9fs_session_info *v9ses, u32 msgsize, break; case RWALK: rcall->params.rwalk.nwqid = buf_get_int16(bufp); + if (rcall->params.rwalk.nwqid > 16) { + eprintk(KERN_ERR, "Rwalk with more than 16 qids: %d\n", + rcall->params.rwalk.nwqid); + return -EPROTO; + } + rcall->params.rwalk.wqids = buf_alloc(dbufp, rcall->params.rwalk.nwqid * sizeof(struct v9fs_qid)); if (rcall->params.rwalk.wqids) @@ -690,19 +692,21 @@ v9fs_deserialize_fcall(struct v9fs_session_info *v9ses, u32 msgsize, case RSTAT: buf_get_int16(bufp); rcall->params.rstat.stat = - deserialize_statb(v9ses, bufp, dbufp); + deserialize_statb(bufp, dbufp, extended); break; case RWSTAT: break; case RERROR: rcall->params.rerror.error = buf_get_stringb(bufp, dbufp); - if (v9ses->extended) + if (extended) rcall->params.rerror.errno = buf_get_int16(bufp); break; } - if (buf_check_overflow(bufp) || buf_check_overflow(dbufp)) + if (buf_check_overflow(bufp) || buf_check_overflow(dbufp)) { + dprintk(DEBUG_ERROR, "buffer overflow\n"); return -EIO; + } return rcall->size; } diff --git a/fs/9p/conv.h b/fs/9p/conv.h index ee849613c61..d5e33e17a68 100644 --- a/fs/9p/conv.h +++ b/fs/9p/conv.h @@ -24,13 +24,12 @@ * */ -int v9fs_deserialize_stat(struct v9fs_session_info *, void *buf, - u32 buflen, struct v9fs_stat *stat, u32 statlen); -int v9fs_serialize_fcall(struct v9fs_session_info *, struct v9fs_fcall *tcall, - void *buf, u32 buflen); -int v9fs_deserialize_fcall(struct v9fs_session_info *, u32 msglen, - void *buf, u32 buflen, struct v9fs_fcall *rcall, - int rcalllen); +int v9fs_deserialize_stat(void *buf, u32 buflen, struct v9fs_stat *stat, + u32 statlen, int extended); +int v9fs_serialize_fcall(struct v9fs_fcall *tcall, void *buf, u32 buflen, + int extended); +int v9fs_deserialize_fcall(void *buf, u32 buflen, struct v9fs_fcall *rcall, + int rcalllen, int extended); /* this one is actually in error.c right now */ int v9fs_errstr2errno(char *errstr); diff --git a/fs/9p/fid.c b/fs/9p/fid.c index d95f8626d17..60ef8aba757 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -164,7 +164,7 @@ static struct v9fs_fid *v9fs_fid_walk_up(struct dentry *dentry) return v9fs_fid_create(dentry, v9ses, fidnum, 0); clunk_fid: - v9fs_t_clunk(v9ses, fidnum, NULL); + v9fs_t_clunk(v9ses, fidnum); return ERR_PTR(err); } diff --git a/fs/9p/mux.c b/fs/9p/mux.c index 8835b576f74..62b6ad0767e 100644 --- a/fs/9p/mux.c +++ b/fs/9p/mux.c @@ -4,7 +4,7 @@ * Protocol Multiplexer * * Copyright (C) 2004 by Eric Van Hensbergen - * Copyright (C) 2004 by Latchesar Ionkov + * Copyright (C) 2004-2005 by Latchesar Ionkov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -38,438 +39,903 @@ #include "conv.h" #include "mux.h" -/** - * dprintcond - print condition of session info - * @v9ses: session info structure - * @req: RPC request structure - * - */ +#define ERREQFLUSH 1 +#define SCHED_TIMEOUT 10 +#define MAXPOLLWADDR 2 + +enum { + Rworksched = 1, /* read work scheduled or running */ + Rpending = 2, /* can read */ + Wworksched = 4, /* write work scheduled or running */ + Wpending = 8, /* can write */ +}; + +struct v9fs_mux_poll_task; + +struct v9fs_req { + int tag; + struct v9fs_fcall *tcall; + struct v9fs_fcall *rcall; + int err; + v9fs_mux_req_callback cb; + void *cba; + struct list_head req_list; +}; + +struct v9fs_mux_data { + spinlock_t lock; + struct list_head mux_list; + struct v9fs_mux_poll_task *poll_task; + int msize; + unsigned char *extended; + struct v9fs_transport *trans; + struct v9fs_idpool tidpool; + int err; + wait_queue_head_t equeue; + struct list_head req_list; + struct list_head unsent_req_list; + int rpos; + char *rbuf; + int wpos; + int wsize; + char *wbuf; + wait_queue_t poll_wait[MAXPOLLWADDR]; + wait_queue_head_t *poll_waddr[MAXPOLLWADDR]; + poll_table pt; + struct work_struct rq; + struct work_struct wq; + unsigned long wsched; +}; + +struct v9fs_mux_poll_task { + struct task_struct *task; + struct list_head mux_list; + int muxnum; +}; + +struct v9fs_mux_rpc { + struct v9fs_mux_data *m; + struct v9fs_req *req; + int err; + struct v9fs_fcall *rcall; + wait_queue_head_t wqueue; +}; + +static int v9fs_poll_proc(void *); +static void v9fs_read_work(void *); +static void v9fs_write_work(void *); +static void v9fs_pollwait(struct file *filp, wait_queue_head_t * wait_address, + poll_table * p); + +static DECLARE_MUTEX(v9fs_mux_task_lock); +static struct workqueue_struct *v9fs_mux_wq; + +static int v9fs_mux_num; +static int v9fs_mux_poll_task_num; +static struct v9fs_mux_poll_task v9fs_mux_poll_tasks[100]; + +void v9fs_mux_global_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(v9fs_mux_poll_tasks); i++) + v9fs_mux_poll_tasks[i].task = NULL; + + v9fs_mux_wq = create_workqueue("v9fs"); +} -static inline int -dprintcond(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req) +void v9fs_mux_global_exit(void) { - dprintk(DEBUG_MUX, "condition: %d, %p\n", v9ses->transport->status, - req->rcall); - return 0; + destroy_workqueue(v9fs_mux_wq); } /** - * xread - force read of a certain number of bytes - * @v9ses: session info structure - * @ptr: pointer to buffer - * @sz: number of bytes to read + * v9fs_mux_calc_poll_procs - calculates the number of polling procs + * based on the number of mounted v9fs filesystems. * - * Chuck Cranor CS-533 project1 + * The current implementation returns sqrt of the number of mounts. */ +inline int v9fs_mux_calc_poll_procs(int muxnum) +{ + int n; + + if (v9fs_mux_poll_task_num) + n = muxnum / v9fs_mux_poll_task_num + + (muxnum % v9fs_mux_poll_task_num ? 1 : 0); + else + n = 1; + + if (n > ARRAY_SIZE(v9fs_mux_poll_tasks)) + n = ARRAY_SIZE(v9fs_mux_poll_tasks); -static int xread(struct v9fs_session_info *v9ses, void *ptr, unsigned long sz) + return n; +} + +static void v9fs_mux_poll_start(struct v9fs_mux_data *m) { - int rd = 0; - int ret = 0; - while (rd < sz) { - ret = v9ses->transport->read(v9ses->transport, ptr, sz - rd); - if (ret <= 0) { - dprintk(DEBUG_ERROR, "xread errno %d\n", ret); - return ret; + int i, n; + struct v9fs_mux_poll_task *vpt, *vptlast; + + dprintk(DEBUG_MUX, "mux %p muxnum %d procnum %d\n", m, v9fs_mux_num, + v9fs_mux_poll_task_num); + up(&v9fs_mux_task_lock); + + n = v9fs_mux_calc_poll_procs(v9fs_mux_num + 1); + if (n > v9fs_mux_poll_task_num) { + for (i = 0; i < ARRAY_SIZE(v9fs_mux_poll_tasks); i++) { + if (v9fs_mux_poll_tasks[i].task == NULL) { + vpt = &v9fs_mux_poll_tasks[i]; + dprintk(DEBUG_MUX, "create proc %p\n", vpt); + vpt->task = kthread_create(v9fs_poll_proc, + vpt, "v9fs-poll"); + INIT_LIST_HEAD(&vpt->mux_list); + vpt->muxnum = 0; + v9fs_mux_poll_task_num++; + wake_up_process(vpt->task); + break; + } } - rd += ret; - ptr += ret; - } - return (rd); -} -/** - * read_message - read a full 9P2000 fcall packet - * @v9ses: session info structure - * @rcall: fcall structure to read into - * @rcalllen: size of fcall buffer - * - */ + if (i >= ARRAY_SIZE(v9fs_mux_poll_tasks)) + dprintk(DEBUG_ERROR, "warning: no free poll slots\n"); + } -static int -read_message(struct v9fs_session_info *v9ses, - struct v9fs_fcall *rcall, int rcalllen) -{ - unsigned char buf[4]; - void *data; - int size = 0; - int res = 0; - - res = xread(v9ses, buf, sizeof(buf)); - if (res < 0) { - dprintk(DEBUG_ERROR, - "Reading of count field failed returned: %d\n", res); - return res; + n = (v9fs_mux_num + 1) / v9fs_mux_poll_task_num + + ((v9fs_mux_num + 1) % v9fs_mux_poll_task_num ? 1 : 0); + + vptlast = NULL; + for (i = 0; i < ARRAY_SIZE(v9fs_mux_poll_tasks); i++) { + vpt = &v9fs_mux_poll_tasks[i]; + if (vpt->task != NULL) { + vptlast = vpt; + if (vpt->muxnum < n) { + dprintk(DEBUG_MUX, "put in proc %d\n", i); + list_add(&m->mux_list, &vpt->mux_list); + vpt->muxnum++; + m->poll_task = vpt; + memset(&m->poll_waddr, 0, sizeof(m->poll_waddr)); + init_poll_funcptr(&m->pt, v9fs_pollwait); + break; + } + } } - if (res < 4) { - dprintk(DEBUG_ERROR, - "Reading of count field failed returned: %d\n", res); - return -EIO; + if (i >= ARRAY_SIZE(v9fs_mux_poll_tasks)) { + dprintk(DEBUG_MUX, "put in proc %d\n", i); + list_add(&m->mux_list, &vptlast->mux_list); + vptlast->muxnum++; + m->poll_task = vpt; + memset(&m->poll_waddr, 0, sizeof(m->poll_waddr)); + init_poll_funcptr(&m->pt, v9fs_pollwait); } - size = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); - dprintk(DEBUG_MUX, "got a packet count: %d\n", size); + v9fs_mux_num++; + down(&v9fs_mux_task_lock); +} - /* adjust for the four bytes of size */ - size -= 4; +static void v9fs_mux_poll_stop(struct v9fs_mux_data *m) +{ + int i; + struct v9fs_mux_poll_task *vpt; + + up(&v9fs_mux_task_lock); + vpt = m->poll_task; + list_del(&m->mux_list); + for(i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) { + if (m->poll_waddr[i] != NULL) { + remove_wait_queue(m->poll_waddr[i], &m->poll_wait[i]); + m->poll_waddr[i] = NULL; + } + } + vpt->muxnum--; + if (!vpt->muxnum) { + dprintk(DEBUG_MUX, "destroy proc %p\n", vpt); + send_sig(SIGKILL, vpt->task, 1); + vpt->task = NULL; + v9fs_mux_poll_task_num--; + } + v9fs_mux_num--; + down(&v9fs_mux_task_lock); +} - if (size > v9ses->maxdata) { - dprintk(DEBUG_ERROR, "packet too big: %d\n", size); - return -E2BIG; +/** + * v9fs_mux_init - allocate and initialize the per-session mux data + * Creates the polling task if this is the first session. + * + * @trans - transport structure + * @msize - maximum message size + * @extended - pointer to the extended flag + */ +struct v9fs_mux_data *v9fs_mux_init(struct v9fs_transport *trans, int msize, + unsigned char *extended) +{ + int i, n; + struct v9fs_mux_data *m, *mtmp; + + dprintk(DEBUG_MUX, "transport %p msize %d\n", trans, msize); + m = kmalloc(sizeof(struct v9fs_mux_data) + 2 * msize, GFP_KERNEL); + if (!m) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&m->lock); + INIT_LIST_HEAD(&m->mux_list); + m->msize = msize; + m->extended = extended; + m->trans = trans; + idr_init(&m->tidpool.pool); + init_MUTEX(&m->tidpool.lock); + m->err = 0; + init_waitqueue_head(&m->equeue); + INIT_LIST_HEAD(&m->req_list); + INIT_LIST_HEAD(&m->unsent_req_list); + m->rpos = 0; + m->rbuf = (char *)m + sizeof(struct v9fs_mux_data); + m->wpos = m->wsize = 0; + m->wbuf = m->rbuf + msize; + INIT_WORK(&m->rq, v9fs_read_work, m); + INIT_WORK(&m->wq, v9fs_write_work, m); + m->wsched = 0; + memset(&m->poll_waddr, 0, sizeof(m->poll_waddr)); + v9fs_mux_poll_start(m); + + n = trans->poll(trans, &m->pt); + if (n & POLLIN) { + dprintk(DEBUG_MUX, "mux %p can read\n", m); + set_bit(Rpending, &m->wsched); } - data = kmalloc(size, GFP_KERNEL); - if (!data) { - eprintk(KERN_WARNING, "out of memory\n"); - return -ENOMEM; + if (n & POLLOUT) { + dprintk(DEBUG_MUX, "mux %p can write\n", m); + set_bit(Wpending, &m->wsched); } - res = xread(v9ses, data, size); - if (res < size) { - dprintk(DEBUG_ERROR, "Reading of fcall failed returned: %d\n", - res); - kfree(data); - return res; + for(i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) { + if (IS_ERR(m->poll_waddr[i])) { + v9fs_mux_poll_stop(m); + mtmp = (void *)m->poll_waddr; /* the error code */ + kfree(m); + m = mtmp; + break; + } } - /* we now have an in-memory string that is the reply. - * deserialize it. There is very little to go wrong at this point - * save for v9fs_alloc errors. - */ - res = v9fs_deserialize_fcall(v9ses, size, data, v9ses->maxdata, - rcall, rcalllen); + return m; +} - kfree(data); +/** + * v9fs_mux_destroy - cancels all pending requests and frees mux resources + */ +void v9fs_mux_destroy(struct v9fs_mux_data *m) +{ + dprintk(DEBUG_MUX, "mux %p prev %p next %p\n", m, + m->mux_list.prev, m->mux_list.next); + v9fs_mux_cancel(m, -ECONNRESET); + + if (!list_empty(&m->req_list)) { + /* wait until all processes waiting on this session exit */ + dprintk(DEBUG_MUX, "mux %p waiting for empty request queue\n", + m); + wait_event_timeout(m->equeue, (list_empty(&m->req_list)), 5000); + dprintk(DEBUG_MUX, "mux %p request queue empty: %d\n", m, + list_empty(&m->req_list)); + } - if (res < 0) - return res; + v9fs_mux_poll_stop(m); + m->trans = NULL; - return 0; + kfree(m); } /** - * v9fs_recv - receive an RPC response for a particular tag - * @v9ses: session info structure - * @req: RPC request structure - * + * v9fs_pollwait - called by files poll operation to add v9fs-poll task + * to files wait queue */ - -static int v9fs_recv(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req) +static void +v9fs_pollwait(struct file *filp, wait_queue_head_t * wait_address, + poll_table * p) { - int ret = 0; - - dprintk(DEBUG_MUX, "waiting for response: %d\n", req->tcall->tag); - ret = wait_event_interruptible(v9ses->read_wait, - ((v9ses->transport->status != Connected) || - (req->rcall != 0) || (req->err < 0) || - dprintcond(v9ses, req))); + int i; + struct v9fs_mux_data *m; - dprintk(DEBUG_MUX, "got it: rcall %p\n", req->rcall); + m = container_of(p, struct v9fs_mux_data, pt); + for(i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) + if (m->poll_waddr[i] == NULL) + break; - spin_lock(&v9ses->muxlock); - list_del(&req->next); - spin_unlock(&v9ses->muxlock); + if (i >= ARRAY_SIZE(m->poll_waddr)) { + dprintk(DEBUG_ERROR, "not enough wait_address slots\n"); + return; + } - if (req->err < 0) - return req->err; + m->poll_waddr[i] = wait_address; - if (v9ses->transport->status == Disconnected) - return -ECONNRESET; + if (!wait_address) { + dprintk(DEBUG_ERROR, "no wait_address\n"); + m->poll_waddr[i] = ERR_PTR(-EIO); + return; + } - return ret; + init_waitqueue_entry(&m->poll_wait[i], m->poll_task->task); + add_wait_queue(wait_address, &m->poll_wait[i]); } /** - * v9fs_send - send a 9P request - * @v9ses: session info structure - * @req: RPC request to send - * + * v9fs_poll_mux - polls a mux and schedules read or write works if necessary */ - -static int v9fs_send(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req) +static inline void v9fs_poll_mux(struct v9fs_mux_data *m) { - int ret = -1; - void *data = NULL; - struct v9fs_fcall *tcall = req->tcall; - - data = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL); - if (!data) - return -ENOMEM; - - tcall->size = 0; /* enforce size recalculation */ - ret = - v9fs_serialize_fcall(v9ses, tcall, data, - v9ses->maxdata + V9FS_IOHDRSZ); - if (ret < 0) - goto free_data; - - spin_lock(&v9ses->muxlock); - list_add(&req->next, &v9ses->mux_fcalls); - spin_unlock(&v9ses->muxlock); - - dprintk(DEBUG_MUX, "sending message: tag %d size %d\n", tcall->tag, - tcall->size); - ret = v9ses->transport->write(v9ses->transport, data, tcall->size); - - if (ret != tcall->size) { - spin_lock(&v9ses->muxlock); - list_del(&req->next); - kfree(req->rcall); + int n; - spin_unlock(&v9ses->muxlock); - if (ret >= 0) - ret = -EREMOTEIO; - } else - ret = 0; + if (m->err < 0) + return; + + n = m->trans->poll(m->trans, NULL); + if (n < 0 || n & (POLLERR | POLLHUP | POLLNVAL)) { + dprintk(DEBUG_MUX, "error mux %p err %d\n", m, n); + if (n >= 0) + n = -ECONNRESET; + v9fs_mux_cancel(m, n); + } + + if (n & POLLIN) { + set_bit(Rpending, &m->wsched); + dprintk(DEBUG_MUX, "mux %p can read\n", m); + if (!test_and_set_bit(Rworksched, &m->wsched)) { + dprintk(DEBUG_MUX, "schedule read work mux %p\n", m); + queue_work(v9fs_mux_wq, &m->rq); + } + } - free_data: - kfree(data); - return ret; + if (n & POLLOUT) { + set_bit(Wpending, &m->wsched); + dprintk(DEBUG_MUX, "mux %p can write\n", m); + if ((m->wsize || !list_empty(&m->unsent_req_list)) + && !test_and_set_bit(Wworksched, &m->wsched)) { + dprintk(DEBUG_MUX, "schedule write work mux %p\n", m); + queue_work(v9fs_mux_wq, &m->wq); + } + } } /** - * v9fs_mux_rpc - send a request, receive a response - * @v9ses: session info structure - * @tcall: fcall to send - * @rcall: buffer to place response into - * + * v9fs_poll_proc - polls all v9fs transports for new events and queues + * the appropriate work to the work queue */ - -long -v9fs_mux_rpc(struct v9fs_session_info *v9ses, struct v9fs_fcall *tcall, - struct v9fs_fcall **rcall) +static int v9fs_poll_proc(void *a) { - int tid = -1; - struct v9fs_fcall *fcall = NULL; - struct v9fs_rpcreq req; - int ret = -1; + struct v9fs_mux_data *m, *mtmp; + struct v9fs_mux_poll_task *vpt; - if (!v9ses) - return -EINVAL; + vpt = a; + dprintk(DEBUG_MUX, "start %p %p\n", current, vpt); + allow_signal(SIGKILL); + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) + break; - if (!v9ses->transport || v9ses->transport->status != Connected) - return -EIO; + list_for_each_entry_safe(m, mtmp, &vpt->mux_list, mux_list) { + v9fs_poll_mux(m); + } + + dprintk(DEBUG_MUX, "sleeping...\n"); + schedule_timeout(SCHED_TIMEOUT * HZ); + } - if (rcall) - *rcall = NULL; + __set_current_state(TASK_RUNNING); + dprintk(DEBUG_MUX, "finish\n"); + return 0; +} - if (tcall->id != TVERSION) { - tid = v9fs_get_idpool(&v9ses->tidpool); - if (tid < 0) - return -ENOMEM; +static inline int v9fs_write_req(struct v9fs_mux_data *m, struct v9fs_req *req) +{ + int n; + + list_move_tail(&req->req_list, &m->req_list); + n = v9fs_serialize_fcall(req->tcall, m->wbuf, m->msize, *m->extended); + if (n < 0) { + req->err = n; + list_del(&req->req_list); + if (req->cb) { + spin_unlock(&m->lock); + (*req->cb) (req->cba, req->tcall, req->rcall, req->err); + req->cb = NULL; + spin_lock(&m->lock); + } else + kfree(req->rcall); + + kfree(req); } - tcall->tag = tid; + return n; +} - req.tcall = tcall; - req.err = 0; - req.rcall = NULL; +/** + * v9fs_write_work - called when a transport can send some data + */ +static void v9fs_write_work(void *a) +{ + int n, err; + struct v9fs_mux_data *m; + struct v9fs_req *req, *rtmp; - ret = v9fs_send(v9ses, &req); + m = a; - if (ret < 0) { - if (tcall->id != TVERSION) - v9fs_put_idpool(tid, &v9ses->tidpool); - dprintk(DEBUG_MUX, "error %d\n", ret); - return ret; + if (m->err < 0) { + clear_bit(Wworksched, &m->wsched); + return; } - ret = v9fs_recv(v9ses, &req); - - fcall = req.rcall; - - dprintk(DEBUG_MUX, "received: tag=%x, ret=%d\n", tcall->tag, ret); - if (ret == -ERESTARTSYS) { - if (v9ses->transport->status != Disconnected - && tcall->id != TFLUSH) { - unsigned long flags; - - dprintk(DEBUG_MUX, "flushing the tag: %d\n", - tcall->tag); - clear_thread_flag(TIF_SIGPENDING); - v9fs_t_flush(v9ses, tcall->tag); - spin_lock_irqsave(¤t->sighand->siglock, flags); - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, - flags); - dprintk(DEBUG_MUX, "flushing done\n"); + if (!m->wsize) { + if (list_empty(&m->unsent_req_list)) { + clear_bit(Wworksched, &m->wsched); + return; } - goto release_req; - } else if (ret < 0) - goto release_req; - - if (!fcall) - ret = -EIO; - else { - if (fcall->id == RERROR) { - ret = v9fs_errstr2errno(fcall->params.rerror.error); - if (ret == 0) { /* string match failed */ - if (fcall->params.rerror.errno) - ret = -(fcall->params.rerror.errno); - else - ret = -ESERVERFAULT; - } - } else if (fcall->id != tcall->id + 1) { - dprintk(DEBUG_ERROR, - "fcall mismatch: expected %d, got %d\n", - tcall->id + 1, fcall->id); - ret = -EIO; + err = 0; + spin_lock(&m->lock); + list_for_each_entry_safe(req, rtmp, &m->unsent_req_list, + req_list) { + err = v9fs_write_req(m, req); + if (err > 0) + break; } + + m->wsize = err; + m->wpos = 0; + spin_unlock(&m->lock); } - release_req: - if (tcall->id != TVERSION) - v9fs_put_idpool(tid, &v9ses->tidpool); - if (rcall) - *rcall = fcall; - else - kfree(fcall); + dprintk(DEBUG_MUX, "mux %p pos %d size %d\n", m, m->wpos, m->wsize); + clear_bit(Wpending, &m->wsched); + err = m->trans->write(m->trans, m->wbuf + m->wpos, m->wsize - m->wpos); + dprintk(DEBUG_MUX, "mux %p sent %d bytes\n", m, err); + if (err == -EAGAIN) { + clear_bit(Wworksched, &m->wsched); + return; + } + + if (err <= 0) + goto error; + + m->wpos += err; + if (m->wpos == m->wsize) + m->wpos = m->wsize = 0; + + if (m->wsize == 0 && !list_empty(&m->unsent_req_list)) { + if (test_and_clear_bit(Wpending, &m->wsched)) + n = POLLOUT; + else + n = m->trans->poll(m->trans, NULL); + + if (n & POLLOUT) { + dprintk(DEBUG_MUX, "schedule write work mux %p\n", m); + queue_work(v9fs_mux_wq, &m->wq); + } else + clear_bit(Wworksched, &m->wsched); + } else + clear_bit(Wworksched, &m->wsched); - return ret; + return; + + error: + v9fs_mux_cancel(m, err); + clear_bit(Wworksched, &m->wsched); } -/** - * v9fs_mux_cancel_requests - cancels all pending requests - * - * @v9ses: session info structure - * @err: error code to return to the requests - */ -void v9fs_mux_cancel_requests(struct v9fs_session_info *v9ses, int err) +static void process_request(struct v9fs_mux_data *m, struct v9fs_req *req) { - struct v9fs_rpcreq *rptr; - struct v9fs_rpcreq *rreq; + int ecode, tag; + char *ename; + + tag = req->tag; + if (req->rcall->id == RERROR && !req->err) { + ecode = req->rcall->params.rerror.errno; + ename = req->rcall->params.rerror.error; - dprintk(DEBUG_MUX, " %d\n", err); - spin_lock(&v9ses->muxlock); - list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) { - rreq->err = err; + dprintk(DEBUG_MUX, "Rerror %s\n", ename); + + if (*m->extended) + req->err = -ecode; + + if (!req->err) { + req->err = v9fs_errstr2errno(ename); + + if (!req->err) { /* string match failed */ + dprintk(DEBUG_ERROR, "unknown error: %s\n", + ename); + } + + if (!req->err) + req->err = -ESERVERFAULT; + } + } else if (req->tcall && req->rcall->id != req->tcall->id + 1) { + dprintk(DEBUG_ERROR, "fcall mismatch: expected %d, got %d\n", + req->tcall->id + 1, req->rcall->id); + if (!req->err) + req->err = -EIO; } - spin_unlock(&v9ses->muxlock); - wake_up_all(&v9ses->read_wait); + + if (req->cb && req->err != ERREQFLUSH) { + dprintk(DEBUG_MUX, "calling callback tcall %p rcall %p\n", + req->tcall, req->rcall); + + (*req->cb) (req->cba, req->tcall, req->rcall, req->err); + req->cb = NULL; + } else + kfree(req->rcall); + + if (tag != V9FS_NOTAG) + v9fs_put_idpool(tag, &m->tidpool); + + wake_up(&m->equeue); + kfree(req); } /** - * v9fs_recvproc - kproc to handle demultiplexing responses - * @data: session info structure - * + * v9fs_read_work - called when there is some data to be read from a transport */ - -static int v9fs_recvproc(void *data) +static void v9fs_read_work(void *a) { - struct v9fs_session_info *v9ses = (struct v9fs_session_info *)data; - struct v9fs_fcall *rcall = NULL; - struct v9fs_rpcreq *rptr; - struct v9fs_rpcreq *req; - struct v9fs_rpcreq *rreq; - int err = 0; + int n, err, rcallen; + struct v9fs_mux_data *m; + struct v9fs_req *req, *rptr, *rreq; + struct v9fs_fcall *rcall; + + m = a; + + if (m->err < 0) + return; + + rcall = NULL; + dprintk(DEBUG_MUX, "start mux %p pos %d\n", m, m->rpos); + clear_bit(Rpending, &m->wsched); + err = m->trans->read(m->trans, m->rbuf + m->rpos, m->msize - m->rpos); + dprintk(DEBUG_MUX, "mux %p got %d bytes\n", m, err); + if (err == -EAGAIN) { + clear_bit(Rworksched, &m->wsched); + return; + } - allow_signal(SIGKILL); - set_current_state(TASK_INTERRUPTIBLE); - complete(&v9ses->proccmpl); - while (!kthread_should_stop() && err >= 0) { - req = rptr = rreq = NULL; + if (err <= 0) + goto error; - rcall = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL); - if (!rcall) { - eprintk(KERN_ERR, "no memory for buffers\n"); + m->rpos += err; + while (m->rpos > 4) { + n = le32_to_cpu(*(__le32 *) m->rbuf); + if (n >= m->msize) { + dprintk(DEBUG_ERROR, + "requested packet size too big: %d\n", n); + err = -EIO; + goto error; + } + + if (m->rpos < n) break; + + rcallen = n + V9FS_FCALLHDRSZ; + rcall = kmalloc(rcallen, GFP_KERNEL); + if (!rcall) { + err = -ENOMEM; + goto error; } - err = read_message(v9ses, rcall, v9ses->maxdata + V9FS_IOHDRSZ); - spin_lock(&v9ses->muxlock); + dump_data(m->rbuf, n); + err = v9fs_deserialize_fcall(m->rbuf, n, rcall, rcallen, + *m->extended); if (err < 0) { - list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) { - rreq->err = err; - } - if(err != -ERESTARTSYS) - eprintk(KERN_ERR, - "Transport error while reading message %d\n", err); - } else { - list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) { - if (rreq->tcall->tag == rcall->tag) { - req = rreq; - req->rcall = rcall; - break; - } - } + kfree(rcall); + goto error; } - if (req && (req->tcall->id == TFLUSH)) { - struct v9fs_rpcreq *treq = NULL; - list_for_each_entry_safe(treq, rptr, &v9ses->mux_fcalls, next) { - if (treq->tcall->tag == - req->tcall->params.tflush.oldtag) { - list_del(&rptr->next); - kfree(treq->rcall); - break; - } + dprintk(DEBUG_MUX, "mux %p fcall id %d tag %d\n", m, rcall->id, + rcall->tag); + + req = NULL; + spin_lock(&m->lock); + list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) { + if (rreq->tag == rcall->tag) { + req = rreq; + req->rcall = rcall; + list_del(&req->req_list); + spin_unlock(&m->lock); + process_request(m, req); + break; } } - spin_unlock(&v9ses->muxlock); - if (!req) { - if (err >= 0) + spin_unlock(&m->lock); + if (err >= 0 && rcall->id != RFLUSH) dprintk(DEBUG_ERROR, - "unexpected response: id %d tag %d\n", - rcall->id, rcall->tag); - + "unexpected response mux %p id %d tag %d\n", + m, rcall->id, rcall->tag); kfree(rcall); } - wake_up_all(&v9ses->read_wait); - set_current_state(TASK_INTERRUPTIBLE); + if (m->rpos > n) + memmove(m->rbuf, m->rbuf + n, m->rpos - n); + m->rpos -= n; } - v9ses->transport->close(v9ses->transport); - - /* Inform all pending processes about the failure */ - wake_up_all(&v9ses->read_wait); - - if (signal_pending(current)) - complete(&v9ses->proccmpl); + if (!list_empty(&m->req_list)) { + if (test_and_clear_bit(Rpending, &m->wsched)) + n = POLLIN; + else + n = m->trans->poll(m->trans, NULL); + + if (n & POLLIN) { + dprintk(DEBUG_MUX, "schedule read work mux %p\n", m); + queue_work(v9fs_mux_wq, &m->rq); + } else + clear_bit(Rworksched, &m->wsched); + } else + clear_bit(Rworksched, &m->wsched); - dprintk(DEBUG_MUX, "recvproc: end\n"); - v9ses->recvproc = NULL; + return; - return err >= 0; + error: + v9fs_mux_cancel(m, err); + clear_bit(Rworksched, &m->wsched); } /** - * v9fs_mux_init - initialize multiplexer (spawn kproc) - * @v9ses: session info structure - * @dev_name: mount device information (to create unique kproc) + * v9fs_send_request - send 9P request + * The function can sleep until the request is scheduled for sending. + * The function can be interrupted. Return from the function is not + * a guarantee that the request is sent succesfully. Can return errors + * that can be retrieved by PTR_ERR macros. * + * @m: mux data + * @tc: request to be sent + * @cb: callback function to call when response is received + * @cba: parameter to pass to the callback function */ +static struct v9fs_req *v9fs_send_request(struct v9fs_mux_data *m, + struct v9fs_fcall *tc, + v9fs_mux_req_callback cb, void *cba) +{ + int n; + struct v9fs_req *req; + + dprintk(DEBUG_MUX, "mux %p task %p tcall %p id %d\n", m, current, + tc, tc->id); + if (m->err < 0) + return ERR_PTR(m->err); + + req = kmalloc(sizeof(struct v9fs_req), GFP_KERNEL); + if (!req) + return ERR_PTR(-ENOMEM); -int v9fs_mux_init(struct v9fs_session_info *v9ses, const char *dev_name) + if (tc->id == TVERSION) + n = V9FS_NOTAG; + else + n = v9fs_get_idpool(&m->tidpool); + + if (n < 0) + return ERR_PTR(-ENOMEM); + + tc->tag = n; + req->tag = n; + req->tcall = tc; + req->rcall = NULL; + req->err = 0; + req->cb = cb; + req->cba = cba; + + spin_lock(&m->lock); + list_add_tail(&req->req_list, &m->unsent_req_list); + spin_unlock(&m->lock); + + if (test_and_clear_bit(Wpending, &m->wsched)) + n = POLLOUT; + else + n = m->trans->poll(m->trans, NULL); + + if (n & POLLOUT && !test_and_set_bit(Wworksched, &m->wsched)) + queue_work(v9fs_mux_wq, &m->wq); + + return req; +} + +static inline void +v9fs_mux_flush_cb(void *a, struct v9fs_fcall *tc, struct v9fs_fcall *rc, + int err) { - char procname[60]; - - strncpy(procname, dev_name, sizeof(procname)); - procname[sizeof(procname) - 1] = 0; - - init_waitqueue_head(&v9ses->read_wait); - init_completion(&v9ses->fcread); - init_completion(&v9ses->proccmpl); - spin_lock_init(&v9ses->muxlock); - INIT_LIST_HEAD(&v9ses->mux_fcalls); - v9ses->recvproc = NULL; - v9ses->curfcall = NULL; - - v9ses->recvproc = kthread_create(v9fs_recvproc, v9ses, - "v9fs_recvproc %s", procname); - - if (IS_ERR(v9ses->recvproc)) { - eprintk(KERN_ERR, "cannot create receiving thread\n"); - v9fs_session_close(v9ses); - return -ECONNABORTED; + v9fs_mux_req_callback cb; + int tag; + struct v9fs_mux_data *m; + struct v9fs_req *req, *rptr; + + m = a; + dprintk(DEBUG_MUX, "mux %p tc %p rc %p err %d oldtag %d\n", m, tc, + rc, err, tc->params.tflush.oldtag); + + spin_lock(&m->lock); + cb = NULL; + tag = tc->params.tflush.oldtag; + list_for_each_entry_safe(req, rptr, &m->req_list, req_list) { + if (req->tag == tag) { + list_del(&req->req_list); + if (req->cb) { + cb = req->cb; + req->cb = NULL; + spin_unlock(&m->lock); + (*cb) (req->cba, req->tcall, req->rcall, + req->err); + } + kfree(req); + wake_up(&m->equeue); + break; + } + } + + if (!cb) + spin_unlock(&m->lock); + + if (v9fs_check_idpool(tag, &m->tidpool)) + v9fs_put_idpool(tag, &m->tidpool); + + kfree(tc); + kfree(rc); +} + +static void +v9fs_mux_flush_request(struct v9fs_mux_data *m, struct v9fs_req *req) +{ + struct v9fs_fcall *fc; + + dprintk(DEBUG_MUX, "mux %p req %p tag %d\n", m, req, req->tag); + + fc = kmalloc(sizeof(struct v9fs_fcall), GFP_KERNEL); + fc->id = TFLUSH; + fc->params.tflush.oldtag = req->tag; + + v9fs_send_request(m, fc, v9fs_mux_flush_cb, m); +} + +static void +v9fs_mux_rpc_cb(void *a, struct v9fs_fcall *tc, struct v9fs_fcall *rc, int err) +{ + struct v9fs_mux_rpc *r; + + if (err == ERREQFLUSH) { + dprintk(DEBUG_MUX, "err req flush\n"); + return; + } + + r = a; + dprintk(DEBUG_MUX, "mux %p req %p tc %p rc %p err %d\n", r->m, r->req, + tc, rc, err); + r->rcall = rc; + r->err = err; + wake_up(&r->wqueue); +} + +/** + * v9fs_mux_rpc - sends 9P request and waits until a response is available. + * The function can be interrupted. + * @m: mux data + * @tc: request to be sent + * @rc: pointer where a pointer to the response is stored + */ +int +v9fs_mux_rpc(struct v9fs_mux_data *m, struct v9fs_fcall *tc, + struct v9fs_fcall **rc) +{ + int err; + unsigned long flags; + struct v9fs_req *req; + struct v9fs_mux_rpc r; + + r.err = 0; + r.rcall = NULL; + r.m = m; + init_waitqueue_head(&r.wqueue); + + if (rc) + *rc = NULL; + + req = v9fs_send_request(m, tc, v9fs_mux_rpc_cb, &r); + if (IS_ERR(req)) { + err = PTR_ERR(req); + dprintk(DEBUG_MUX, "error %d\n", err); + return PTR_ERR(req); + } + + r.req = req; + dprintk(DEBUG_MUX, "mux %p tc %p tag %d rpc %p req %p\n", m, tc, + req->tag, &r, req); + err = wait_event_interruptible(r.wqueue, r.rcall != NULL || r.err < 0); + if (r.err < 0) + err = r.err; + + if (err == -ERESTARTSYS && m->trans->status == Connected && m->err == 0) { + spin_lock(&m->lock); + req->tcall = NULL; + req->err = ERREQFLUSH; + spin_unlock(&m->lock); + + clear_thread_flag(TIF_SIGPENDING); + v9fs_mux_flush_request(m, req); + spin_lock_irqsave(¤t->sighand->siglock, flags); + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); } - wake_up_process(v9ses->recvproc); - wait_for_completion(&v9ses->proccmpl); + if (!err) { + if (r.rcall) + dprintk(DEBUG_MUX, "got response id %d tag %d\n", + r.rcall->id, r.rcall->tag); + + if (rc) + *rc = r.rcall; + else + kfree(r.rcall); + } else { + kfree(r.rcall); + dprintk(DEBUG_MUX, "got error %d\n", err); + if (err > 0) + err = -EIO; + } + + return err; +} + +/** + * v9fs_mux_rpcnb - sends 9P request without waiting for response. + * @m: mux data + * @tc: request to be sent + * @cb: callback function to be called when response arrives + * @cba: value to pass to the callback function + */ +int v9fs_mux_rpcnb(struct v9fs_mux_data *m, struct v9fs_fcall *tc, + v9fs_mux_req_callback cb, void *a) +{ + int err; + struct v9fs_req *req; + + req = v9fs_send_request(m, tc, cb, a); + if (IS_ERR(req)) { + err = PTR_ERR(req); + dprintk(DEBUG_MUX, "error %d\n", err); + return PTR_ERR(req); + } + dprintk(DEBUG_MUX, "mux %p tc %p tag %d\n", m, tc, req->tag); return 0; } + +/** + * v9fs_mux_cancel - cancel all pending requests with error + * @m: mux data + * @err: error code + */ +void v9fs_mux_cancel(struct v9fs_mux_data *m, int err) +{ + struct v9fs_req *req, *rtmp; + LIST_HEAD(cancel_list); + + dprintk(DEBUG_MUX, "mux %p err %d\n", m, err); + m->err = err; + spin_lock(&m->lock); + list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) { + list_move(&req->req_list, &cancel_list); + } + spin_unlock(&m->lock); + + list_for_each_entry_safe(req, rtmp, &cancel_list, req_list) { + list_del(&req->req_list); + if (!req->err) + req->err = err; + + if (req->cb) + (*req->cb) (req->cba, req->tcall, req->rcall, req->err); + else + kfree(req->rcall); + + kfree(req); + } + + wake_up(&m->equeue); +} diff --git a/fs/9p/mux.h b/fs/9p/mux.h index 4994cb10bad..02b13b14b05 100644 --- a/fs/9p/mux.h +++ b/fs/9p/mux.h @@ -3,6 +3,7 @@ * * Multiplexer Definitions * + * Copyright (C) 2005 by Latchesar Ionkov * Copyright (C) 2004 by Eric Van Hensbergen * * This program is free software; you can redistribute it and/or modify @@ -23,19 +24,34 @@ * */ -/* structure to manage each RPC transaction */ +struct v9fs_mux_data; -struct v9fs_rpcreq { - struct v9fs_fcall *tcall; - struct v9fs_fcall *rcall; - int err; /* error code if response failed */ +/** + * v9fs_mux_req_callback - callback function that is called when the + * response of a request is received. The callback is called from + * a workqueue and shouldn't block. + * + * @a - the pointer that was specified when the request was send to be + * passed to the callback + * @tc - request call + * @rc - response call + * @err - error code (non-zero if error occured) + */ +typedef void (*v9fs_mux_req_callback)(void *a, struct v9fs_fcall *tc, + struct v9fs_fcall *rc, int err); + +void v9fs_mux_global_init(void); +void v9fs_mux_global_exit(void); - /* XXX - could we put scatter/gather buffers here? */ +struct v9fs_mux_data *v9fs_mux_init(struct v9fs_transport *trans, int msize, + unsigned char *extended); +void v9fs_mux_destroy(struct v9fs_mux_data *); - struct list_head next; -}; +int v9fs_mux_send(struct v9fs_mux_data *m, struct v9fs_fcall *tc); +struct v9fs_fcall *v9fs_mux_recv(struct v9fs_mux_data *m); +int v9fs_mux_rpc(struct v9fs_mux_data *m, struct v9fs_fcall *tc, struct v9fs_fcall **rc); +int v9fs_mux_rpcnb(struct v9fs_mux_data *m, struct v9fs_fcall *tc, + v9fs_mux_req_callback cb, void *a); -int v9fs_mux_init(struct v9fs_session_info *v9ses, const char *dev_name); -long v9fs_mux_rpc(struct v9fs_session_info *v9ses, - struct v9fs_fcall *tcall, struct v9fs_fcall **rcall); -void v9fs_mux_cancel_requests(struct v9fs_session_info *v9ses, int err); +void v9fs_mux_flush(struct v9fs_mux_data *m, int sendflush); +void v9fs_mux_cancel(struct v9fs_mux_data *m, int err); diff --git a/fs/9p/trans_fd.c b/fs/9p/trans_fd.c index b7ffb985958..1a28ef97a3d 100644 --- a/fs/9p/trans_fd.c +++ b/fs/9p/trans_fd.c @@ -3,6 +3,7 @@ * * File Descriptor Transport Layer * + * Copyright (C) 2005 by Latchesar Ionkov * Copyright (C) 2005 by Eric Van Hensbergen * * This program is free software; you can redistribute it and/or modify @@ -106,9 +107,6 @@ v9fs_fd_init(struct v9fs_session_info *v9ses, const char *addr, char *data) return -ENOPROTOOPT; } - sema_init(&trans->writelock, 1); - sema_init(&trans->readlock, 1); - ts = kmalloc(sizeof(struct v9fs_trans_fd), GFP_KERNEL); if (!ts) @@ -163,10 +161,55 @@ static void v9fs_fd_close(struct v9fs_transport *trans) kfree(ts); } +static unsigned int +v9fs_fd_poll(struct v9fs_transport *trans, struct poll_table_struct *pt) +{ + int ret, n; + struct v9fs_trans_fd *ts; + mm_segment_t oldfs; + + if (!trans) + return -EIO; + + ts = trans->priv; + if (trans->status != Connected || !ts) + return -EIO; + + oldfs = get_fs(); + set_fs(get_ds()); + + if (!ts->in_file->f_op || !ts->in_file->f_op->poll) { + ret = -EIO; + goto end; + } + + ret = ts->in_file->f_op->poll(ts->in_file, pt); + + if (ts->out_file != ts->in_file) { + if (!ts->out_file->f_op || !ts->out_file->f_op->poll) { + ret = -EIO; + goto end; + } + + n = ts->out_file->f_op->poll(ts->out_file, pt); + + ret &= ~POLLOUT; + n &= ~POLLIN; + + ret |= n; + } + +end: + set_fs(oldfs); + return ret; +} + + struct v9fs_transport v9fs_trans_fd = { .init = v9fs_fd_init, .write = v9fs_fd_send, .read = v9fs_fd_recv, .close = v9fs_fd_close, + .poll = v9fs_fd_poll, }; diff --git a/fs/9p/trans_sock.c b/fs/9p/trans_sock.c index 6a9a75d40f7..9ef404c75c8 100644 --- a/fs/9p/trans_sock.c +++ b/fs/9p/trans_sock.c @@ -3,6 +3,7 @@ * * Socket Transport Layer * + * Copyright (C) 2004-2005 by Latchesar Ionkov * Copyright (C) 2004 by Eric Van Hensbergen * Copyright (C) 1997-2002 by Ron Minnich * Copyright (C) 1995, 1996 by Olaf Kirch @@ -36,6 +37,7 @@ #include #include #include +#include #include "debug.h" #include "v9fs.h" @@ -45,6 +47,7 @@ struct v9fs_trans_sock { struct socket *s; + struct file *filp; }; /** @@ -57,41 +60,26 @@ struct v9fs_trans_sock { static int v9fs_sock_recv(struct v9fs_transport *trans, void *v, int len) { - struct msghdr msg; - struct kvec iov; - int result; - mm_segment_t oldfs; - struct v9fs_trans_sock *ts = trans ? trans->priv : NULL; + int ret; + struct v9fs_trans_sock *ts; - if (trans->status == Disconnected) + if (!trans || trans->status == Disconnected) { + dprintk(DEBUG_ERROR, "disconnected ...\n"); return -EREMOTEIO; + } - result = -EINVAL; - - oldfs = get_fs(); - set_fs(get_ds()); - - iov.iov_base = v; - iov.iov_len = len; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_namelen = 0; - msg.msg_flags = MSG_NOSIGNAL; - - result = kernel_recvmsg(ts->s, &msg, &iov, 1, len, 0); + ts = trans->priv; - dprintk(DEBUG_TRANS, "socket state %d\n", ts->s->state); - set_fs(oldfs); + if (!(ts->filp->f_flags & O_NONBLOCK)) + dprintk(DEBUG_ERROR, "blocking read ...\n"); - if (result <= 0) { - if (result != -ERESTARTSYS) + ret = kernel_read(ts->filp, ts->filp->f_pos, v, len); + if (ret <= 0) { + if (ret != -ERESTARTSYS && ret != -EAGAIN) trans->status = Disconnected; } - return result; + return ret; } /** @@ -104,40 +92,73 @@ static int v9fs_sock_recv(struct v9fs_transport *trans, void *v, int len) static int v9fs_sock_send(struct v9fs_transport *trans, void *v, int len) { - struct kvec iov; - struct msghdr msg; - int result = -1; + int ret; mm_segment_t oldfs; - struct v9fs_trans_sock *ts = trans ? trans->priv : NULL; + struct v9fs_trans_sock *ts; - dprintk(DEBUG_TRANS, "Sending packet size %d (%x)\n", len, len); - dump_data(v, len); + if (!trans || trans->status == Disconnected) { + dprintk(DEBUG_ERROR, "disconnected ...\n"); + return -EREMOTEIO; + } + + ts = trans->priv; + if (!ts) { + dprintk(DEBUG_ERROR, "no transport ...\n"); + return -EREMOTEIO; + } - down(&trans->writelock); + if (!(ts->filp->f_flags & O_NONBLOCK)) + dprintk(DEBUG_ERROR, "blocking write ...\n"); + dump_data(v, len); oldfs = get_fs(); set_fs(get_ds()); - iov.iov_base = v; - iov.iov_len = len; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_namelen = 0; - msg.msg_flags = MSG_NOSIGNAL; - result = kernel_sendmsg(ts->s, &msg, &iov, 1, len); + ret = vfs_write(ts->filp, (void __user *)v, len, &ts->filp->f_pos); set_fs(oldfs); - if (result < 0) { - if (result != -ERESTARTSYS) + if (ret < 0) { + if (ret != -ERESTARTSYS) trans->status = Disconnected; } - up(&trans->writelock); - return result; + return ret; +} + +static unsigned int v9fs_sock_poll(struct v9fs_transport *trans, + struct poll_table_struct *pt) { + + int ret; + struct v9fs_trans_sock *ts; + mm_segment_t oldfs; + + if (!trans) { + dprintk(DEBUG_ERROR, "no transport\n"); + return -EIO; + } + + ts = trans->priv; + if (trans->status != Connected || !ts) { + dprintk(DEBUG_ERROR, "transport disconnected: %d\n", trans->status); + return -EIO; + } + + oldfs = get_fs(); + set_fs(get_ds()); + + if (!ts->filp->f_op || !ts->filp->f_op->poll) { + dprintk(DEBUG_ERROR, "no poll operation\n"); + ret = -EIO; + goto end; + } + + ret = ts->filp->f_op->poll(ts->filp, pt); + +end: + set_fs(oldfs); + return ret; } + /** * v9fs_tcp_init - initialize TCP socket * @v9ses: session information @@ -154,9 +175,9 @@ v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, char *data) int rc = 0; struct v9fs_trans_sock *ts = NULL; struct v9fs_transport *trans = v9ses->transport; + int fd; - sema_init(&trans->writelock, 1); - sema_init(&trans->readlock, 1); + trans->status = Disconnected; ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL); @@ -165,6 +186,7 @@ v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, char *data) trans->priv = ts; ts->s = NULL; + ts->filp = NULL; if (!addr) return -EINVAL; @@ -185,7 +207,18 @@ v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, char *data) return rc; } csocket->sk->sk_allocation = GFP_NOIO; + + fd = sock_map_fd(csocket); + if (fd < 0) { + sock_release(csocket); + kfree(ts); + trans->priv = NULL; + return fd; + } + ts->s = csocket; + ts->filp = fget(fd); + ts->filp->f_flags |= O_NONBLOCK; trans->status = Connected; return 0; @@ -203,7 +236,7 @@ static int v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name, char *data) { - int rc; + int rc, fd; struct socket *csocket; struct sockaddr_un sun_server; struct v9fs_transport *trans; @@ -213,6 +246,8 @@ v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name, csocket = NULL; trans = v9ses->transport; + trans->status = Disconnected; + if (strlen(dev_name) > UNIX_PATH_MAX) { eprintk(KERN_ERR, "v9fs_trans_unix: address too long: %s\n", dev_name); @@ -225,9 +260,7 @@ v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name, trans->priv = ts; ts->s = NULL; - - sema_init(&trans->writelock, 1); - sema_init(&trans->readlock, 1); + ts->filp = NULL; sun_server.sun_family = PF_UNIX; strcpy(sun_server.sun_path, dev_name); @@ -241,7 +274,18 @@ v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name, return rc; } csocket->sk->sk_allocation = GFP_NOIO; + + fd = sock_map_fd(csocket); + if (fd < 0) { + sock_release(csocket); + kfree(ts); + trans->priv = NULL; + return fd; + } + ts->s = csocket; + ts->filp = fget(fd); + ts->filp->f_flags |= O_NONBLOCK; trans->status = Connected; return 0; @@ -262,12 +306,11 @@ static void v9fs_sock_close(struct v9fs_transport *trans) ts = trans->priv; - if ((ts) && (ts->s)) { - dprintk(DEBUG_TRANS, "closing the socket %p\n", ts->s); - sock_release(ts->s); + if ((ts) && (ts->filp)) { + fput(ts->filp); + ts->filp = NULL; ts->s = NULL; trans->status = Disconnected; - dprintk(DEBUG_TRANS, "socket closed\n"); } kfree(ts); @@ -280,6 +323,7 @@ struct v9fs_transport v9fs_trans_tcp = { .write = v9fs_sock_send, .read = v9fs_sock_recv, .close = v9fs_sock_close, + .poll = v9fs_sock_poll, }; struct v9fs_transport v9fs_trans_unix = { @@ -287,4 +331,5 @@ struct v9fs_transport v9fs_trans_unix = { .write = v9fs_sock_send, .read = v9fs_sock_recv, .close = v9fs_sock_close, + .poll = v9fs_sock_poll, }; diff --git a/fs/9p/transport.h b/fs/9p/transport.h index 9e9cd418efd..91fcdb94b36 100644 --- a/fs/9p/transport.h +++ b/fs/9p/transport.h @@ -3,6 +3,7 @@ * * Transport Definition * + * Copyright (C) 2005 by Latchesar Ionkov * Copyright (C) 2004 by Eric Van Hensbergen * * This program is free software; you can redistribute it and/or modify @@ -31,14 +32,13 @@ enum v9fs_transport_status { struct v9fs_transport { enum v9fs_transport_status status; - struct semaphore writelock; - struct semaphore readlock; void *priv; int (*init) (struct v9fs_session_info *, const char *, char *); int (*write) (struct v9fs_transport *, void *, int); int (*read) (struct v9fs_transport *, void *, int); void (*close) (struct v9fs_transport *); + unsigned int (*poll)(struct v9fs_transport *, struct poll_table_struct *); }; extern struct v9fs_transport v9fs_trans_tcp; diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 418c3743fde..5e0f79355fd 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -213,7 +213,8 @@ retry: return -1; } - error = idr_get_new(&p->pool, NULL, &i); + /* no need to store exactly p, we just need something non-null */ + error = idr_get_new(&p->pool, p, &i); up(&p->lock); if (error == -EAGAIN) @@ -242,6 +243,16 @@ void v9fs_put_idpool(int id, struct v9fs_idpool *p) up(&p->lock); } +/** + * v9fs_check_idpool - check if the specified id is available + * @id - id to check + * @p - pool + */ +int v9fs_check_idpool(int id, struct v9fs_idpool *p) +{ + return idr_find(&p->pool, id) != NULL; +} + /** * v9fs_session_init - initialize session * @v9ses: session information structure @@ -281,9 +292,6 @@ v9fs_session_init(struct v9fs_session_info *v9ses, /* id pools that are session-dependent: FIDs and TIDs */ idr_init(&v9ses->fidpool.pool); init_MUTEX(&v9ses->fidpool.lock); - idr_init(&v9ses->tidpool.pool); - init_MUTEX(&v9ses->tidpool.lock); - switch (v9ses->proto) { case PROTO_TCP: @@ -320,7 +328,12 @@ v9fs_session_init(struct v9fs_session_info *v9ses, v9ses->shutdown = 0; v9ses->session_hung = 0; - if ((retval = v9fs_mux_init(v9ses, dev_name)) < 0) { + v9ses->mux = v9fs_mux_init(v9ses->transport, v9ses->maxdata + V9FS_IOHDRSZ, + &v9ses->extended); + + if (IS_ERR(v9ses->mux)) { + retval = PTR_ERR(v9ses->mux); + v9ses->mux = NULL; dprintk(DEBUG_ERROR, "problem initializing mux\n"); goto SessCleanUp; } @@ -381,7 +394,7 @@ v9fs_session_init(struct v9fs_session_info *v9ses, } if (v9ses->afid != ~0) { - if (v9fs_t_clunk(v9ses, v9ses->afid, NULL)) + if (v9fs_t_clunk(v9ses, v9ses->afid)) dprintk(DEBUG_ERROR, "clunk failed\n"); } @@ -403,13 +416,16 @@ v9fs_session_init(struct v9fs_session_info *v9ses, void v9fs_session_close(struct v9fs_session_info *v9ses) { - if (v9ses->recvproc) { - send_sig(SIGKILL, v9ses->recvproc, 1); - wait_for_completion(&v9ses->proccmpl); + if (v9ses->mux) { + v9fs_mux_destroy(v9ses->mux); + v9ses->mux = NULL; } - if (v9ses->transport) + if (v9ses->transport) { v9ses->transport->close(v9ses->transport); + kfree(v9ses->transport); + v9ses->transport = NULL; + } __putname(v9ses->name); __putname(v9ses->remotename); @@ -420,8 +436,9 @@ void v9fs_session_close(struct v9fs_session_info *v9ses) * and cancel all pending requests. */ void v9fs_session_cancel(struct v9fs_session_info *v9ses) { + dprintk(DEBUG_ERROR, "cancel session %p\n", v9ses); v9ses->transport->status = Disconnected; - v9fs_mux_cancel_requests(v9ses, -EIO); + v9fs_mux_cancel(v9ses->mux, -EIO); } extern int v9fs_error_init(void); @@ -437,6 +454,7 @@ static int __init init_v9fs(void) printk(KERN_INFO "Installing v9fs 9P2000 file system support\n"); + v9fs_mux_global_init(); return register_filesystem(&v9fs_fs_type); } @@ -447,6 +465,7 @@ static int __init init_v9fs(void) static void __exit exit_v9fs(void) { + v9fs_mux_global_exit(); unregister_filesystem(&v9fs_fs_type); } diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index 45dcef42bdd..f337da7a0ee 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -57,24 +57,14 @@ struct v9fs_session_info { /* book keeping */ struct v9fs_idpool fidpool; /* The FID pool for file descriptors */ - struct v9fs_idpool tidpool; /* The TID pool for transactions ids */ - /* transport information */ struct v9fs_transport *transport; + struct v9fs_mux_data *mux; int inprogress; /* session in progress => true */ int shutdown; /* session shutting down. no more attaches. */ unsigned char session_hung; - - /* mux private data */ - struct v9fs_fcall *curfcall; - wait_queue_head_t read_wait; - struct completion fcread; - struct completion proccmpl; - struct task_struct *recvproc; - - spinlock_t muxlock; - struct list_head mux_fcalls; + struct dentry *debugfs_dir; }; /* possible values of ->proto */ @@ -84,11 +74,14 @@ enum { PROTO_FD, }; +extern struct dentry *v9fs_debugfs_root; + int v9fs_session_init(struct v9fs_session_info *, const char *, char *); struct v9fs_session_info *v9fs_inode2v9ses(struct inode *); void v9fs_session_close(struct v9fs_session_info *v9ses); int v9fs_get_idpool(struct v9fs_idpool *p); void v9fs_put_idpool(int id, struct v9fs_idpool *p); +int v9fs_check_idpool(int id, struct v9fs_idpool *p); void v9fs_session_cancel(struct v9fs_session_info *v9ses); #define V9FS_MAGIC 0x01021997 diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c index a6aa947de0f..4887df76739 100644 --- a/fs/9p/vfs_dentry.c +++ b/fs/9p/vfs_dentry.c @@ -95,24 +95,21 @@ static int v9fs_dentry_validate(struct dentry *dentry, struct nameidata *nd) void v9fs_dentry_release(struct dentry *dentry) { + int err; + dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry); if (dentry->d_fsdata != NULL) { struct list_head *fid_list = dentry->d_fsdata; struct v9fs_fid *temp = NULL; struct v9fs_fid *current_fid = NULL; - struct v9fs_fcall *fcall = NULL; list_for_each_entry_safe(current_fid, temp, fid_list, list) { - if (v9fs_t_clunk - (current_fid->v9ses, current_fid->fid, &fcall)) - dprintk(DEBUG_ERROR, "clunk failed: %s\n", - FCALL_ERROR(fcall)); + err = v9fs_t_clunk(current_fid->v9ses, current_fid->fid); - v9fs_put_idpool(current_fid->fid, - ¤t_fid->v9ses->fidpool); + if (err < 0) + dprintk(DEBUG_ERROR, "clunk failed: %d\n", err); - kfree(fcall); v9fs_fid_destroy(current_fid); } diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index 17089d1905f..3893dd307dd 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -74,7 +74,7 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) struct inode *inode = filp->f_dentry->d_inode; struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); struct v9fs_fid *file = filp->private_data; - unsigned int i, n; + unsigned int i, n, s; int fid = -1; int ret = 0; struct v9fs_stat *mi = NULL; @@ -97,9 +97,9 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) n = file->rdir_fcall->params.rread.count; i = file->rdir_fpos; while (i < n) { - int s = v9fs_deserialize_stat(v9ses, - file->rdir_fcall->params.rread.data + i, - n - i, mi, v9ses->maxdata); + s = v9fs_deserialize_stat( + file->rdir_fcall->params.rread.data + i, + n - i, mi, v9ses->maxdata, v9ses->extended); if (s == 0) { dprintk(DEBUG_ERROR, @@ -141,9 +141,8 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) n = ret; i = 0; while (i < n) { - int s = v9fs_deserialize_stat(v9ses, - fcall->params.rread.data + i, n - i, mi, - v9ses->maxdata); + s = v9fs_deserialize_stat(fcall->params.rread.data + i, + n - i, mi, v9ses->maxdata, v9ses->extended); if (s == 0) { dprintk(DEBUG_ERROR, @@ -199,11 +198,9 @@ int v9fs_dir_release(struct inode *inode, struct file *filp) dprintk(DEBUG_VFS, "fidopen: %d v9f->fid: %d\n", fid->fidopen, fid->fid); - if (v9fs_t_clunk(v9ses, fidnum, NULL)) + if (v9fs_t_clunk(v9ses, fidnum)) dprintk(DEBUG_ERROR, "clunk failed\n"); - v9fs_put_idpool(fid->fid, &v9ses->fidpool); - kfree(fid->rdir_fcall); kfree(fid); diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 0ea965c3bb7..466002a1fe3 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -318,6 +318,7 @@ v9fs_create(struct inode *dir, int result = 0; unsigned int iounit = 0; int wfidno = -1; + int err; perm = unixmode2p9mode(v9ses, perm); @@ -356,6 +357,7 @@ v9fs_create(struct inode *dir, } kfree(fcall); + fcall = NULL; result = v9fs_t_create(v9ses, newfid, (char *)file_dentry->d_name.name, perm, open_mode, &fcall); @@ -369,16 +371,23 @@ v9fs_create(struct inode *dir, iounit = fcall->params.rcreate.iounit; qid = fcall->params.rcreate.qid; kfree(fcall); + fcall = NULL; - fid = v9fs_fid_create(file_dentry, v9ses, newfid, 1); - dprintk(DEBUG_VFS, "fid %p %d\n", fid, fid->fidcreate); - if (!fid) { - result = -ENOMEM; - goto CleanUpFid; - } + if (!(perm&V9FS_DMDIR)) { + fid = v9fs_fid_create(file_dentry, v9ses, newfid, 1); + dprintk(DEBUG_VFS, "fid %p %d\n", fid, fid->fidcreate); + if (!fid) { + result = -ENOMEM; + goto CleanUpFid; + } - fid->qid = qid; - fid->iounit = iounit; + fid->qid = qid; + fid->iounit = iounit; + } else { + err = v9fs_t_clunk(v9ses, newfid); + if (err < 0) + dprintk(DEBUG_ERROR, "clunk for mkdir failed: %d\n", err); + } /* walk to the newly created file and put the fid in the dentry */ wfidno = v9fs_get_idpool(&v9ses->fidpool); @@ -388,18 +397,19 @@ v9fs_create(struct inode *dir, } result = v9fs_t_walk(v9ses, dirfidnum, wfidno, - (char *) file_dentry->d_name.name, NULL); + (char *) file_dentry->d_name.name, &fcall); if (result < 0) { dprintk(DEBUG_ERROR, "clone error: %s\n", FCALL_ERROR(fcall)); v9fs_put_idpool(wfidno, &v9ses->fidpool); wfidno = -1; goto CleanUpFid; } + kfree(fcall); + fcall = NULL; if (!v9fs_fid_create(file_dentry, v9ses, wfidno, 0)) { - if (!v9fs_t_clunk(v9ses, newfid, &fcall)) { - v9fs_put_idpool(wfidno, &v9ses->fidpool); - } + v9fs_t_clunk(v9ses, newfid); + v9fs_put_idpool(wfidno, &v9ses->fidpool); goto CleanUpFid; } @@ -431,40 +441,21 @@ v9fs_create(struct inode *dir, file_dentry->d_op = &v9fs_dentry_operations; d_instantiate(file_dentry, file_inode); - if (perm & V9FS_DMDIR) { - if (!v9fs_t_clunk(v9ses, newfid, &fcall)) - v9fs_put_idpool(newfid, &v9ses->fidpool); - else - dprintk(DEBUG_ERROR, "clunk for mkdir failed: %s\n", - FCALL_ERROR(fcall)); - kfree(fcall); - fid->fidopen = 0; - fid->fidcreate = 0; - d_drop(file_dentry); - } - return 0; CleanUpFid: kfree(fcall); + fcall = NULL; if (newfid >= 0) { - if (!v9fs_t_clunk(v9ses, newfid, &fcall)) - v9fs_put_idpool(newfid, &v9ses->fidpool); - else - dprintk(DEBUG_ERROR, "clunk failed: %s\n", - FCALL_ERROR(fcall)); - - kfree(fcall); + err = v9fs_t_clunk(v9ses, newfid); + if (err < 0) + dprintk(DEBUG_ERROR, "clunk failed: %d\n", err); } if (wfidno >= 0) { - if (!v9fs_t_clunk(v9ses, wfidno, &fcall)) - v9fs_put_idpool(wfidno, &v9ses->fidpool); - else - dprintk(DEBUG_ERROR, "clunk failed: %s\n", - FCALL_ERROR(fcall)); - - kfree(fcall); + err = v9fs_t_clunk(v9ses, wfidno); + if (err < 0) + dprintk(DEBUG_ERROR, "clunk failed: %d\n", err); } return result; } @@ -972,6 +963,7 @@ v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); struct v9fs_fcall *fcall = NULL; struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); + int err; dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, symname); @@ -1004,9 +996,9 @@ v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) kfree(fcall); - if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) { - dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n", - FCALL_ERROR(fcall)); + err = v9fs_t_clunk(v9ses, newfid->fid); + if (err < 0) { + dprintk(DEBUG_ERROR, "clunk for symlink failed: %d\n", err); goto FreeFcall; } @@ -1180,6 +1172,7 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry); struct v9fs_fid *newfid = NULL; char *symname = __getname(); + int err; dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, old_dentry->d_name.name); @@ -1216,9 +1209,10 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, kfree(fcall); - if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) { - dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n", - FCALL_ERROR(fcall)); + err = v9fs_t_clunk(v9ses, newfid->fid); + + if (err < 0) { + dprintk(DEBUG_ERROR, "clunk for symlink failed: %d\n", err); goto FreeMem; } @@ -1252,6 +1246,7 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) struct v9fs_fcall *fcall = NULL; struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); char *symname = __getname(); + int err; dprintk(DEBUG_VFS, " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino, dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev)); @@ -1310,9 +1305,9 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) /* need to update dcache so we show up */ kfree(fcall); - if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) { - dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n", - FCALL_ERROR(fcall)); + err = v9fs_t_clunk(v9ses, newfid->fid); + if (err < 0) { + dprintk(DEBUG_ERROR, "clunk for symlink failed: %d\n", err); goto FreeMem; } diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 82c5b008407..83b6edd0988 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -129,6 +129,7 @@ static struct super_block *v9fs_get_sb(struct file_system_type if ((newfid = v9fs_session_init(v9ses, dev_name, data)) < 0) { dprintk(DEBUG_ERROR, "problem initiating session\n"); + kfree(v9ses); return ERR_PTR(newfid); } @@ -157,7 +158,7 @@ static struct super_block *v9fs_get_sb(struct file_system_type stat_result = v9fs_t_stat(v9ses, newfid, &fcall); if (stat_result < 0) { dprintk(DEBUG_ERROR, "stat error\n"); - v9fs_t_clunk(v9ses, newfid, NULL); + v9fs_t_clunk(v9ses, newfid); v9fs_put_idpool(newfid, &v9ses->fidpool); } else { /* Setup the Root Inode */ -- cgit From d8da097afb765654c866062148fd98b11db9003e Mon Sep 17 00:00:00 2001 From: Latchesar Ionkov Date: Sun, 8 Jan 2006 01:04:59 -0800 Subject: [PATCH] v9fs: fix fid management in v9fs_create v9fs_create doesn't manage correctly the fids when it is called to create a directory.. The fid created by the create 9P call (newfid) and the one created by walking to already created file (wfidno) are not used consistently. This patch cleans up the usage of newfid and wfidno. Signed-off-by: Latchesar Ionkov Cc: Eric Van Hensbergen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/9p/vfs_inode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 466002a1fe3..f11edde6432 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -385,13 +385,14 @@ v9fs_create(struct inode *dir, fid->iounit = iounit; } else { err = v9fs_t_clunk(v9ses, newfid); + newfid = -1; if (err < 0) dprintk(DEBUG_ERROR, "clunk for mkdir failed: %d\n", err); } /* walk to the newly created file and put the fid in the dentry */ wfidno = v9fs_get_idpool(&v9ses->fidpool); - if (newfid < 0) { + if (wfidno < 0) { eprintk(KERN_WARNING, "no free fids available\n"); return -ENOSPC; } @@ -408,7 +409,6 @@ v9fs_create(struct inode *dir, fcall = NULL; if (!v9fs_fid_create(file_dentry, v9ses, wfidno, 0)) { - v9fs_t_clunk(v9ses, newfid); v9fs_put_idpool(wfidno, &v9ses->fidpool); goto CleanUpFid; @@ -419,7 +419,7 @@ v9fs_create(struct inode *dir, (perm & V9FS_DMDEVICE)) return 0; - result = v9fs_t_stat(v9ses, newfid, &fcall); + result = v9fs_t_stat(v9ses, wfidno, &fcall); if (result < 0) { dprintk(DEBUG_ERROR, "stat error: %s(%d)\n", FCALL_ERROR(fcall), result); -- cgit From 531b1094b74365dcc55fa464d28a9a2497ae825d Mon Sep 17 00:00:00 2001 From: Latchesar Ionkov Date: Sun, 8 Jan 2006 01:05:00 -0800 Subject: [PATCH] v9fs: zero copy implementation Performance enhancement reducing the number of copies in the data and stat paths. Signed-off-by: Latchesar Ionkov Cc: Eric Van Hensbergen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/9p/9p.c | 302 ++++++++++-------- fs/9p/9p.h | 75 +++-- fs/9p/Makefile | 10 +- fs/9p/conv.c | 895 +++++++++++++++++++++++++++++++---------------------- fs/9p/conv.h | 28 +- fs/9p/debug.h | 23 +- fs/9p/error.c | 10 +- fs/9p/error.h | 3 +- fs/9p/fid.c | 3 - fs/9p/mux.c | 157 +++++----- fs/9p/trans_sock.c | 1 - fs/9p/v9fs.c | 3 +- fs/9p/v9fs_vfs.h | 5 +- fs/9p/vfs_dentry.c | 4 +- fs/9p/vfs_dir.c | 31 +- fs/9p/vfs_file.c | 25 +- fs/9p/vfs_inode.c | 545 +++++++++++--------------------- fs/9p/vfs_super.c | 10 +- 18 files changed, 1083 insertions(+), 1047 deletions(-) (limited to 'fs') diff --git a/fs/9p/9p.c b/fs/9p/9p.c index a3a1ac61072..dc3ce44ec83 100644 --- a/fs/9p/9p.c +++ b/fs/9p/9p.c @@ -1,8 +1,9 @@ /* * linux/fs/9p/9p.c * - * This file contains functions 9P2000 functions + * This file contains functions to perform synchronous 9P calls * + * Copyright (C) 2004 by Latchesar Ionkov * Copyright (C) 2004 by Eric Van Hensbergen * Copyright (C) 2002 by Ron Minnich * @@ -33,6 +34,7 @@ #include "debug.h" #include "v9fs.h" #include "9p.h" +#include "conv.h" #include "mux.h" /** @@ -46,17 +48,21 @@ int v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, - char *version, struct v9fs_fcall **fcall) + char *version, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; dprintk(DEBUG_9P, "msize: %d version: %s\n", msize, version); - msg.id = TVERSION; - msg.tag = ~0; - msg.params.tversion.msize = msize; - msg.params.tversion.version = version; + tc = v9fs_create_tversion(msize, version); - return v9fs_mux_rpc(v9ses->mux, &msg, fcall); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); + + return ret; } /** @@ -72,19 +78,23 @@ v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, int v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname, - u32 fid, u32 afid, struct v9fs_fcall **fcall) + u32 fid, u32 afid, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall* tc; dprintk(DEBUG_9P, "uname '%s' aname '%s' fid %d afid %d\n", uname, aname, fid, afid); - msg.id = TATTACH; - msg.params.tattach.fid = fid; - msg.params.tattach.afid = afid; - msg.params.tattach.uname = uname; - msg.params.tattach.aname = aname; - return v9fs_mux_rpc(v9ses->mux, &msg, fcall); + ret = -ENOMEM; + tc = v9fs_create_tattach(fid, afid, uname, aname); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); + + return ret; } static void v9fs_t_clunk_cb(void *a, struct v9fs_fcall *tc, @@ -117,24 +127,28 @@ static void v9fs_t_clunk_cb(void *a, struct v9fs_fcall *tc, * @fcall: pointer to response fcall pointer * */ + int v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid) { - int err; + int ret; struct v9fs_fcall *tc, *rc; - tc = kmalloc(sizeof(struct v9fs_fcall), GFP_KERNEL); - dprintk(DEBUG_9P, "fid %d\n", fid); - tc->id = TCLUNK; - tc->params.tclunk.fid = fid; - err = v9fs_mux_rpc(v9ses->mux, tc, &rc); - if (err >= 0) { - v9fs_t_clunk_cb(v9ses, tc, rc, 0); - } + ret = -ENOMEM; + rc = NULL; + tc = v9fs_create_tclunk(fid); + if (!IS_ERR(tc)) + ret = v9fs_mux_rpc(v9ses->mux, tc, &rc); + else + ret = PTR_ERR(tc); + + if (ret) + dprintk(DEBUG_ERROR, "failed fid %d err %d\n", fid, ret); - return err; + v9fs_t_clunk_cb(v9ses, tc, rc, ret); + return ret; } /** @@ -144,14 +158,22 @@ v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid) * */ -int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 tag) +int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 oldtag) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; - dprintk(DEBUG_9P, "oldtag %d\n", tag); - msg.id = TFLUSH; - msg.params.tflush.oldtag = tag; - return v9fs_mux_rpc(v9ses->mux, &msg, NULL); + dprintk(DEBUG_9P, "oldtag %d\n", oldtag); + + ret = -ENOMEM; + tc = v9fs_create_tflush(oldtag); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, NULL); + kfree(tc); + } else + ret = PTR_ERR(tc); + + return ret; } /** @@ -163,17 +185,22 @@ int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 tag) */ int -v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, struct v9fs_fcall **fcall) +v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; dprintk(DEBUG_9P, "fid %d\n", fid); - if (fcall) - *fcall = NULL; - msg.id = TSTAT; - msg.params.tstat.fid = fid; - return v9fs_mux_rpc(v9ses->mux, &msg, fcall); + ret = -ENOMEM; + tc = v9fs_create_tstat(fid); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); + + return ret; } /** @@ -187,16 +214,22 @@ v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, struct v9fs_fcall **fcall) int v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_stat *stat, struct v9fs_fcall **fcall) + struct v9fs_wstat *wstat, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; - dprintk(DEBUG_9P, "fid %d length %d\n", fid, (int)stat->length); - msg.id = TWSTAT; - msg.params.twstat.fid = fid; - msg.params.twstat.stat = stat; + dprintk(DEBUG_9P, "fid %d\n", fid); + + ret = -ENOMEM; + tc = v9fs_create_twstat(fid, wstat, v9ses->extended); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); - return v9fs_mux_rpc(v9ses->mux, &msg, fcall); + return ret; } /** @@ -213,23 +246,28 @@ v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid, int v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid, - char *name, struct v9fs_fcall **fcall) + char *name, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; + int nwname; dprintk(DEBUG_9P, "fid %d newfid %d wname '%s'\n", fid, newfid, name); - msg.id = TWALK; - msg.params.twalk.fid = fid; - msg.params.twalk.newfid = newfid; - - if (name) { - msg.params.twalk.nwname = 1; - msg.params.twalk.wnames = &name; - } else { - msg.params.twalk.nwname = 0; - } - - return v9fs_mux_rpc(v9ses->mux, &msg, fcall); + + if (name) + nwname = 1; + else + nwname = 0; + + ret = -ENOMEM; + tc = v9fs_create_twalk(fid, newfid, nwname, &name); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); + + return ret; } /** @@ -244,19 +282,22 @@ v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid, int v9fs_t_open(struct v9fs_session_info *v9ses, u32 fid, u8 mode, - struct v9fs_fcall **fcall) + struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; - int errorno = -1; + int ret; + struct v9fs_fcall *tc; dprintk(DEBUG_9P, "fid %d mode %d\n", fid, mode); - msg.id = TOPEN; - msg.params.topen.fid = fid; - msg.params.topen.mode = mode; - errorno = v9fs_mux_rpc(v9ses->mux, &msg, fcall); + ret = -ENOMEM; + tc = v9fs_create_topen(fid, mode); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); - return errorno; + return ret; } /** @@ -269,14 +310,22 @@ v9fs_t_open(struct v9fs_session_info *v9ses, u32 fid, u8 mode, int v9fs_t_remove(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_fcall **fcall) + struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; dprintk(DEBUG_9P, "fid %d\n", fid); - msg.id = TREMOVE; - msg.params.tremove.fid = fid; - return v9fs_mux_rpc(v9ses->mux, &msg, fcall); + + ret = -ENOMEM; + tc = v9fs_create_tremove(fid); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); + + return ret; } /** @@ -292,20 +341,23 @@ v9fs_t_remove(struct v9fs_session_info *v9ses, u32 fid, int v9fs_t_create(struct v9fs_session_info *v9ses, u32 fid, char *name, - u32 perm, u8 mode, struct v9fs_fcall **fcall) + u32 perm, u8 mode, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; + int ret; + struct v9fs_fcall *tc; dprintk(DEBUG_9P, "fid %d name '%s' perm %x mode %d\n", fid, name, perm, mode); - msg.id = TCREATE; - msg.params.tcreate.fid = fid; - msg.params.tcreate.name = name; - msg.params.tcreate.perm = perm; - msg.params.tcreate.mode = mode; + ret = -ENOMEM; + tc = v9fs_create_tcreate(fid, name, perm, mode); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); + kfree(tc); + } else + ret = PTR_ERR(tc); - return v9fs_mux_rpc(v9ses->mux, &msg, fcall); + return ret; } /** @@ -320,31 +372,30 @@ v9fs_t_create(struct v9fs_session_info *v9ses, u32 fid, char *name, int v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, u64 offset, - u32 count, struct v9fs_fcall **fcall) + u32 count, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; - struct v9fs_fcall *rc = NULL; - long errorno = -1; - - dprintk(DEBUG_9P, "fid %d offset 0x%lx count 0x%x\n", fid, - (long unsigned int)offset, count); - msg.id = TREAD; - msg.params.tread.fid = fid; - msg.params.tread.offset = offset; - msg.params.tread.count = count; - errorno = v9fs_mux_rpc(v9ses->mux, &msg, &rc); - - if (!errorno) { - errorno = rc->params.rread.count; - dump_data(rc->params.rread.data, rc->params.rread.count); - } - - if (fcall) - *fcall = rc; - else - kfree(rc); + int ret; + struct v9fs_fcall *tc, *rc; - return errorno; + dprintk(DEBUG_9P, "fid %d offset 0x%llux count 0x%x\n", fid, + (long long unsigned) offset, count); + + ret = -ENOMEM; + tc = v9fs_create_tread(fid, offset, count); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, &rc); + if (!ret) + ret = rc->params.rread.count; + if (rcp) + *rcp = rc; + else + kfree(rc); + + kfree(tc); + } else + ret = PTR_ERR(tc); + + return ret; } /** @@ -358,32 +409,31 @@ v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, u64 offset, */ int -v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, - u64 offset, u32 count, void *data, struct v9fs_fcall **fcall) +v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, u64 offset, u32 count, + const char __user *data, struct v9fs_fcall **rcp) { - struct v9fs_fcall msg; - struct v9fs_fcall *rc = NULL; - long errorno = -1; + int ret; + struct v9fs_fcall *tc, *rc; - dprintk(DEBUG_9P, "fid %d offset 0x%llx count 0x%x\n", fid, - (unsigned long long)offset, count); - dump_data(data, count); + dprintk(DEBUG_9P, "fid %d offset 0x%llux count 0x%x\n", fid, + (long long unsigned) offset, count); - msg.id = TWRITE; - msg.params.twrite.fid = fid; - msg.params.twrite.offset = offset; - msg.params.twrite.count = count; - msg.params.twrite.data = data; + ret = -ENOMEM; + tc = v9fs_create_twrite(fid, offset, count, data); + if (!IS_ERR(tc)) { + ret = v9fs_mux_rpc(v9ses->mux, tc, &rc); - errorno = v9fs_mux_rpc(v9ses->mux, &msg, &rc); + if (!ret) + ret = rc->params.rwrite.count; + if (rcp) + *rcp = rc; + else + kfree(rc); - if (!errorno) - errorno = rc->params.rwrite.count; + kfree(tc); + } else + ret = PTR_ERR(tc); - if (fcall) - *fcall = rc; - else - kfree(rc); - - return errorno; + return ret; } + diff --git a/fs/9p/9p.h b/fs/9p/9p.h index 6355392786e..007ff639777 100644 --- a/fs/9p/9p.h +++ b/fs/9p/9p.h @@ -3,6 +3,7 @@ * * 9P protocol definitions. * + * Copyright (C) 2005 by Latchesar Ionkov * Copyright (C) 2004 by Eric Van Hensbergen * Copyright (C) 2002 by Ron Minnich * @@ -102,10 +103,16 @@ enum { #define V9FS_NOTAG (u16)(~0) #define V9FS_NOFID (u32)(~0) +#define V9FS_MAXWELEM 16 /* ample room for Twrite/Rread header (iounit) */ #define V9FS_IOHDRSZ 24 +struct v9fs_str { + u16 len; + char *str; +}; + /* qids are the unique ID for a file (like an inode */ struct v9fs_qid { u8 type; @@ -115,6 +122,29 @@ struct v9fs_qid { /* Plan 9 file metadata (stat) structure */ struct v9fs_stat { + u16 size; + u16 type; + u32 dev; + struct v9fs_qid qid; + u32 mode; + u32 atime; + u32 mtime; + u64 length; + struct v9fs_str name; + struct v9fs_str uid; + struct v9fs_str gid; + struct v9fs_str muid; + struct v9fs_str extension; /* 9p2000.u extensions */ + u32 n_uid; /* 9p2000.u extensions */ + u32 n_gid; /* 9p2000.u extensions */ + u32 n_muid; /* 9p2000.u extensions */ +}; + +/* file metadata (stat) structure used to create Twstat message + The is similar to v9fs_stat, but the strings don't point to + the same memory block and should be freed separately +*/ +struct v9fs_wstat { u16 size; u16 type; u32 dev; @@ -131,25 +161,24 @@ struct v9fs_stat { u32 n_uid; /* 9p2000.u extensions */ u32 n_gid; /* 9p2000.u extensions */ u32 n_muid; /* 9p2000.u extensions */ - char data[0]; }; /* Structures for Protocol Operations */ struct Tversion { u32 msize; - char *version; + struct v9fs_str version; }; struct Rversion { u32 msize; - char *version; + struct v9fs_str version; }; struct Tauth { u32 afid; - char *uname; - char *aname; + struct v9fs_str uname; + struct v9fs_str aname; }; struct Rauth { @@ -157,12 +186,12 @@ struct Rauth { }; struct Rerror { - char *error; + struct v9fs_str error; u32 errno; /* 9p2000.u extension */ }; struct Tflush { - u32 oldtag; + u16 oldtag; }; struct Rflush { @@ -171,8 +200,8 @@ struct Rflush { struct Tattach { u32 fid; u32 afid; - char *uname; - char *aname; + struct v9fs_str uname; + struct v9fs_str aname; }; struct Rattach { @@ -182,13 +211,13 @@ struct Rattach { struct Twalk { u32 fid; u32 newfid; - u32 nwname; - char **wnames; + u16 nwname; + struct v9fs_str wnames[16]; }; struct Rwalk { - u32 nwqid; - struct v9fs_qid *wqids; + u16 nwqid; + struct v9fs_qid wqids[16]; }; struct Topen { @@ -203,7 +232,7 @@ struct Ropen { struct Tcreate { u32 fid; - char *name; + struct v9fs_str name; u32 perm; u8 mode; }; @@ -254,12 +283,12 @@ struct Tstat { }; struct Rstat { - struct v9fs_stat *stat; + struct v9fs_stat stat; }; struct Twstat { u32 fid; - struct v9fs_stat *stat; + struct v9fs_stat stat; }; struct Rwstat { @@ -274,6 +303,7 @@ struct v9fs_fcall { u32 size; u8 id; u16 tag; + void *sdata; union { struct Tversion tversion; @@ -306,10 +336,12 @@ struct v9fs_fcall { } params; }; -#define V9FS_FCALLHDRSZ (sizeof(struct v9fs_fcall) + \ - sizeof(struct v9fs_stat) + 16*sizeof(struct v9fs_qid) + 16) +#define PRINT_FCALL_ERROR(s, fcall) dprintk(DEBUG_ERROR, "%s: %.*s\n", s, \ + fcall?fcall->params.rerror.error.len:0, \ + fcall?fcall->params.rerror.error.str:""); -#define FCALL_ERROR(fcall) (fcall ? fcall->params.rerror.error : "") +char *v9fs_str_copy(char *buf, int buflen, struct v9fs_str *str); +int v9fs_str_compare(char *buf, struct v9fs_str *str); int v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, char *version, struct v9fs_fcall **rcall); @@ -325,7 +357,7 @@ int v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, struct v9fs_fcall **rcall); int v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid, - struct v9fs_stat *stat, struct v9fs_fcall **rcall); + struct v9fs_wstat *wstat, struct v9fs_fcall **rcall); int v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid, char *name, struct v9fs_fcall **rcall); @@ -343,4 +375,5 @@ int v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, u64 offset, u32 count, struct v9fs_fcall **rcall); int v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, u64 offset, - u32 count, void *data, struct v9fs_fcall **rcall); + u32 count, const char __user * data, + struct v9fs_fcall **rcall); diff --git a/fs/9p/Makefile b/fs/9p/Makefile index e4e4ffe5a7d..3d023089707 100644 --- a/fs/9p/Makefile +++ b/fs/9p/Makefile @@ -1,17 +1,17 @@ obj-$(CONFIG_9P_FS) := 9p2000.o 9p2000-objs := \ + trans_fd.o \ + trans_sock.o \ + mux.o \ + 9p.o \ + conv.o \ vfs_super.o \ vfs_inode.o \ vfs_file.o \ vfs_dir.o \ vfs_dentry.o \ error.o \ - mux.o \ - trans_fd.o \ - trans_sock.o \ - 9p.o \ - conv.o \ v9fs.o \ fid.o diff --git a/fs/9p/conv.c b/fs/9p/conv.c index 1b9b15dfeaf..f62434d435b 100644 --- a/fs/9p/conv.c +++ b/fs/9p/conv.c @@ -30,7 +30,7 @@ #include #include #include - +#include #include "debug.h" #include "v9fs.h" #include "9p.h" @@ -45,6 +45,37 @@ struct cbuf { unsigned char *ep; }; +char *v9fs_str_copy(char *buf, int buflen, struct v9fs_str *str) +{ + int n; + + if (buflen < str->len) + n = buflen; + else + n = str->len; + + memmove(buf, str->str, n - 1); + + return buf; +} + +int v9fs_str_compare(char *buf, struct v9fs_str *str) +{ + int n, ret; + + ret = strncmp(buf, str->str, str->len); + + if (!ret) { + n = strlen(buf); + if (n < str->len) + ret = -1; + else if (n > str->len) + ret = 1; + } + + return ret; +} + static inline void buf_init(struct cbuf *buf, void *data, int datalen) { buf->sp = buf->p = data; @@ -58,12 +89,12 @@ static inline int buf_check_overflow(struct cbuf *buf) static inline int buf_check_size(struct cbuf *buf, int len) { - if (buf->p+len > buf->ep) { - if (buf->p < buf->ep) { - eprintk(KERN_ERR, "buffer overflow\n"); - buf->p = buf->ep + 1; - return 0; - } + if (buf->p + len > buf->ep && buf->p < buf->ep) { + eprintk(KERN_ERR, "buffer overflow: want %d has %d\n", + len, (int)(buf->ep - buf->p)); + dump_stack(); + buf->p = buf->ep + 1; + return 0; } return 1; @@ -127,14 +158,6 @@ static inline void buf_put_string(struct cbuf *buf, const char *s) buf_put_stringn(buf, s, strlen(s)); } -static inline void buf_put_data(struct cbuf *buf, void *data, u32 datalen) -{ - if (buf_check_size(buf, datalen)) { - memcpy(buf->p, data, datalen); - buf->p += datalen; - } -} - static inline u8 buf_get_int8(struct cbuf *buf) { u8 ret = 0; @@ -183,85 +206,37 @@ static inline u64 buf_get_int64(struct cbuf *buf) return ret; } -static inline int -buf_get_string(struct cbuf *buf, char *data, unsigned int datalen) -{ - u16 len = 0; - - len = buf_get_int16(buf); - if (!buf_check_overflow(buf) && buf_check_size(buf, len) && len+1>datalen) { - memcpy(data, buf->p, len); - data[len] = 0; - buf->p += len; - len++; - } - - return len; -} - -static inline char *buf_get_stringb(struct cbuf *buf, struct cbuf *sbuf) -{ - char *ret; - u16 len; - - ret = NULL; - len = buf_get_int16(buf); - - if (!buf_check_overflow(buf) && buf_check_size(buf, len) && - buf_check_size(sbuf, len + 1)) { - - memcpy(sbuf->p, buf->p, len); - sbuf->p[len] = 0; - ret = sbuf->p; - buf->p += len; - sbuf->p += len + 1; - } - - return ret; -} - -static inline int buf_get_data(struct cbuf *buf, void *data, int datalen) +static inline void buf_get_str(struct cbuf *buf, struct v9fs_str *vstr) { - int ret = 0; - - if (buf_check_size(buf, datalen)) { - memcpy(data, buf->p, datalen); - buf->p += datalen; - ret = datalen; + vstr->len = buf_get_int16(buf); + if (!buf_check_overflow(buf) && buf_check_size(buf, vstr->len)) { + vstr->str = buf->p; + buf->p += vstr->len; + } else { + vstr->len = 0; + vstr->str = NULL; } - - return ret; } -static inline void *buf_get_datab(struct cbuf *buf, struct cbuf *dbuf, - int datalen) +static inline void buf_get_qid(struct cbuf *bufp, struct v9fs_qid *qid) { - char *ret = NULL; - int n = 0; - - if (buf_check_size(dbuf, datalen)) { - n = buf_get_data(buf, dbuf->p, datalen); - if (n > 0) { - ret = dbuf->p; - dbuf->p += n; - } - } - - return ret; + qid->type = buf_get_int8(bufp); + qid->version = buf_get_int32(bufp); + qid->path = buf_get_int64(bufp); } /** - * v9fs_size_stat - calculate the size of a variable length stat struct + * v9fs_size_wstat - calculate the size of a variable length stat struct * @stat: metadata (stat) structure * @extended: non-zero if 9P2000.u * */ -static int v9fs_size_stat(struct v9fs_stat *stat, int extended) +static int v9fs_size_wstat(struct v9fs_wstat *wstat, int extended) { int size = 0; - if (stat == NULL) { + if (wstat == NULL) { eprintk(KERN_ERR, "v9fs_size_stat: got a NULL stat pointer\n"); return 0; } @@ -278,81 +253,38 @@ static int v9fs_size_stat(struct v9fs_stat *stat, int extended) 8 + /* length[8] */ 8; /* minimum sum of string lengths */ - if (stat->name) - size += strlen(stat->name); - if (stat->uid) - size += strlen(stat->uid); - if (stat->gid) - size += strlen(stat->gid); - if (stat->muid) - size += strlen(stat->muid); + if (wstat->name) + size += strlen(wstat->name); + if (wstat->uid) + size += strlen(wstat->uid); + if (wstat->gid) + size += strlen(wstat->gid); + if (wstat->muid) + size += strlen(wstat->muid); if (extended) { size += 4 + /* n_uid[4] */ 4 + /* n_gid[4] */ 4 + /* n_muid[4] */ 2; /* string length of extension[4] */ - if (stat->extension) - size += strlen(stat->extension); + if (wstat->extension) + size += strlen(wstat->extension); } return size; } /** - * serialize_stat - safely format a stat structure for transmission - * @stat: metadata (stat) structure - * @bufp: buffer to serialize structure into - * @extended: non-zero if 9P2000.u - * - */ - -static int -serialize_stat(struct v9fs_stat *stat, struct cbuf *bufp, int extended) -{ - buf_put_int16(bufp, stat->size); - buf_put_int16(bufp, stat->type); - buf_put_int32(bufp, stat->dev); - buf_put_int8(bufp, stat->qid.type); - buf_put_int32(bufp, stat->qid.version); - buf_put_int64(bufp, stat->qid.path); - buf_put_int32(bufp, stat->mode); - buf_put_int32(bufp, stat->atime); - buf_put_int32(bufp, stat->mtime); - buf_put_int64(bufp, stat->length); - - buf_put_string(bufp, stat->name); - buf_put_string(bufp, stat->uid); - buf_put_string(bufp, stat->gid); - buf_put_string(bufp, stat->muid); - - if (extended) { - buf_put_string(bufp, stat->extension); - buf_put_int32(bufp, stat->n_uid); - buf_put_int32(bufp, stat->n_gid); - buf_put_int32(bufp, stat->n_muid); - } - - if (buf_check_overflow(bufp)) - return 0; - - return stat->size; -} - -/** - * deserialize_stat - safely decode a recieved metadata (stat) structure + * buf_get_stat - safely decode a recieved metadata (stat) structure * @bufp: buffer to deserialize * @stat: metadata (stat) structure - * @dbufp: buffer to deserialize variable strings into * @extended: non-zero if 9P2000.u * */ -static inline int -deserialize_stat(struct cbuf *bufp, struct v9fs_stat *stat, - struct cbuf *dbufp, int extended) +static inline void +buf_get_stat(struct cbuf *bufp, struct v9fs_stat *stat, int extended) { - stat->size = buf_get_int16(bufp); stat->type = buf_get_int16(bufp); stat->dev = buf_get_int32(bufp); @@ -363,45 +295,17 @@ deserialize_stat(struct cbuf *bufp, struct v9fs_stat *stat, stat->atime = buf_get_int32(bufp); stat->mtime = buf_get_int32(bufp); stat->length = buf_get_int64(bufp); - stat->name = buf_get_stringb(bufp, dbufp); - stat->uid = buf_get_stringb(bufp, dbufp); - stat->gid = buf_get_stringb(bufp, dbufp); - stat->muid = buf_get_stringb(bufp, dbufp); + buf_get_str(bufp, &stat->name); + buf_get_str(bufp, &stat->uid); + buf_get_str(bufp, &stat->gid); + buf_get_str(bufp, &stat->muid); if (extended) { - stat->extension = buf_get_stringb(bufp, dbufp); + buf_get_str(bufp, &stat->extension); stat->n_uid = buf_get_int32(bufp); stat->n_gid = buf_get_int32(bufp); stat->n_muid = buf_get_int32(bufp); } - - if (buf_check_overflow(bufp) || buf_check_overflow(dbufp)) - return 0; - - return stat->size + 2; -} - -/** - * deserialize_statb - wrapper for decoding a received metadata structure - * @bufp: buffer to deserialize - * @dbufp: buffer to deserialize variable strings into - * @extended: non-zero if 9P2000.u - * - */ - -static inline struct v9fs_stat *deserialize_statb(struct cbuf *bufp, - struct cbuf *dbufp, - int extended) -{ - struct v9fs_stat *ret = buf_alloc(dbufp, sizeof(struct v9fs_stat)); - - if (ret) { - int n = deserialize_stat(bufp, ret, dbufp, extended); - if (n <= 0) - return NULL; - } - - return ret; } /** @@ -409,194 +313,27 @@ static inline struct v9fs_stat *deserialize_statb(struct cbuf *bufp, * @buf: buffer to deserialize * @buflen: length of received buffer * @stat: metadata structure to decode into - * @statlen: length of destination metadata structure * @extended: non-zero if 9P2000.u * + * Note: stat will point to the buf region. */ -int v9fs_deserialize_stat(void *buf, u32 buflen, struct v9fs_stat *stat, - u32 statlen, int extended) +int +v9fs_deserialize_stat(void *buf, u32 buflen, struct v9fs_stat *stat, + int extended) { struct cbuf buffer; struct cbuf *bufp = &buffer; - struct cbuf dbuffer; - struct cbuf *dbufp = &dbuffer; + unsigned char *p; buf_init(bufp, buf, buflen); - buf_init(dbufp, (char *)stat + sizeof(struct v9fs_stat), - statlen - sizeof(struct v9fs_stat)); - - return deserialize_stat(bufp, stat, dbufp, extended); -} - -static inline int v9fs_size_fcall(struct v9fs_fcall *fcall, int extended) -{ - int size = 4 + 1 + 2; /* size[4] msg[1] tag[2] */ - int i = 0; + p = bufp->p; + buf_get_stat(bufp, stat, extended); - switch (fcall->id) { - default: - eprintk(KERN_ERR, "bad msg type %d\n", fcall->id); + if (buf_check_overflow(bufp)) return 0; - case TVERSION: /* msize[4] version[s] */ - size += 4 + 2 + strlen(fcall->params.tversion.version); - break; - case TAUTH: /* afid[4] uname[s] aname[s] */ - size += 4 + 2 + strlen(fcall->params.tauth.uname) + - 2 + strlen(fcall->params.tauth.aname); - break; - case TFLUSH: /* oldtag[2] */ - size += 2; - break; - case TATTACH: /* fid[4] afid[4] uname[s] aname[s] */ - size += 4 + 4 + 2 + strlen(fcall->params.tattach.uname) + - 2 + strlen(fcall->params.tattach.aname); - break; - case TWALK: /* fid[4] newfid[4] nwname[2] nwname*(wname[s]) */ - size += 4 + 4 + 2; - /* now compute total for the array of names */ - for (i = 0; i < fcall->params.twalk.nwname; i++) - size += 2 + strlen(fcall->params.twalk.wnames[i]); - break; - case TOPEN: /* fid[4] mode[1] */ - size += 4 + 1; - break; - case TCREATE: /* fid[4] name[s] perm[4] mode[1] */ - size += 4 + 2 + strlen(fcall->params.tcreate.name) + 4 + 1; - break; - case TREAD: /* fid[4] offset[8] count[4] */ - size += 4 + 8 + 4; - break; - case TWRITE: /* fid[4] offset[8] count[4] data[count] */ - size += 4 + 8 + 4 + fcall->params.twrite.count; - break; - case TCLUNK: /* fid[4] */ - size += 4; - break; - case TREMOVE: /* fid[4] */ - size += 4; - break; - case TSTAT: /* fid[4] */ - size += 4; - break; - case TWSTAT: /* fid[4] stat[n] */ - fcall->params.twstat.stat->size = - v9fs_size_stat(fcall->params.twstat.stat, extended); - size += 4 + 2 + 2 + fcall->params.twstat.stat->size; - } - return size; -} - -/* - * v9fs_serialize_fcall - marshall fcall struct into a packet - * @fcall: structure to convert - * @data: buffer to serialize fcall into - * @datalen: length of buffer to serialize fcall into - * @extended: non-zero if 9P2000.u - * - */ - -int -v9fs_serialize_fcall(struct v9fs_fcall *fcall, void *data, u32 datalen, - int extended) -{ - int i = 0; - struct v9fs_stat *stat = NULL; - struct cbuf buffer; - struct cbuf *bufp = &buffer; - - buf_init(bufp, data, datalen); - - if (!fcall) { - eprintk(KERN_ERR, "no fcall\n"); - return -EINVAL; - } - - fcall->size = v9fs_size_fcall(fcall, extended); - - buf_put_int32(bufp, fcall->size); - buf_put_int8(bufp, fcall->id); - buf_put_int16(bufp, fcall->tag); - - dprintk(DEBUG_CONV, "size %d id %d tag %d\n", fcall->size, fcall->id, - fcall->tag); - - /* now encode it */ - switch (fcall->id) { - default: - eprintk(KERN_ERR, "bad msg type: %d\n", fcall->id); - return -EPROTO; - case TVERSION: - buf_put_int32(bufp, fcall->params.tversion.msize); - buf_put_string(bufp, fcall->params.tversion.version); - break; - case TAUTH: - buf_put_int32(bufp, fcall->params.tauth.afid); - buf_put_string(bufp, fcall->params.tauth.uname); - buf_put_string(bufp, fcall->params.tauth.aname); - break; - case TFLUSH: - buf_put_int16(bufp, fcall->params.tflush.oldtag); - break; - case TATTACH: - buf_put_int32(bufp, fcall->params.tattach.fid); - buf_put_int32(bufp, fcall->params.tattach.afid); - buf_put_string(bufp, fcall->params.tattach.uname); - buf_put_string(bufp, fcall->params.tattach.aname); - break; - case TWALK: - buf_put_int32(bufp, fcall->params.twalk.fid); - buf_put_int32(bufp, fcall->params.twalk.newfid); - buf_put_int16(bufp, fcall->params.twalk.nwname); - for (i = 0; i < fcall->params.twalk.nwname; i++) - buf_put_string(bufp, fcall->params.twalk.wnames[i]); - break; - case TOPEN: - buf_put_int32(bufp, fcall->params.topen.fid); - buf_put_int8(bufp, fcall->params.topen.mode); - break; - case TCREATE: - buf_put_int32(bufp, fcall->params.tcreate.fid); - buf_put_string(bufp, fcall->params.tcreate.name); - buf_put_int32(bufp, fcall->params.tcreate.perm); - buf_put_int8(bufp, fcall->params.tcreate.mode); - break; - case TREAD: - buf_put_int32(bufp, fcall->params.tread.fid); - buf_put_int64(bufp, fcall->params.tread.offset); - buf_put_int32(bufp, fcall->params.tread.count); - break; - case TWRITE: - buf_put_int32(bufp, fcall->params.twrite.fid); - buf_put_int64(bufp, fcall->params.twrite.offset); - buf_put_int32(bufp, fcall->params.twrite.count); - buf_put_data(bufp, fcall->params.twrite.data, - fcall->params.twrite.count); - break; - case TCLUNK: - buf_put_int32(bufp, fcall->params.tclunk.fid); - break; - case TREMOVE: - buf_put_int32(bufp, fcall->params.tremove.fid); - break; - case TSTAT: - buf_put_int32(bufp, fcall->params.tstat.fid); - break; - case TWSTAT: - buf_put_int32(bufp, fcall->params.twstat.fid); - stat = fcall->params.twstat.stat; - - buf_put_int16(bufp, stat->size + 2); - serialize_stat(stat, bufp, extended); - break; - } - - if (buf_check_overflow(bufp)) { - dprintk(DEBUG_ERROR, "buffer overflow\n"); - return -EIO; - } - - return fcall->size; + else + return bufp->p - p; } /** @@ -611,18 +348,14 @@ v9fs_serialize_fcall(struct v9fs_fcall *fcall, void *data, u32 datalen, int v9fs_deserialize_fcall(void *buf, u32 buflen, struct v9fs_fcall *rcall, - int rcalllen, int extended) + int extended) { struct cbuf buffer; struct cbuf *bufp = &buffer; - struct cbuf dbuffer; - struct cbuf *dbufp = &dbuffer; int i = 0; buf_init(bufp, buf, buflen); - buf_init(dbufp, (char *)rcall + sizeof(struct v9fs_fcall), - rcalllen - sizeof(struct v9fs_fcall)); rcall->size = buf_get_int32(bufp); rcall->id = buf_get_int8(bufp); @@ -630,13 +363,14 @@ v9fs_deserialize_fcall(void *buf, u32 buflen, struct v9fs_fcall *rcall, dprintk(DEBUG_CONV, "size %d id %d tag %d\n", rcall->size, rcall->id, rcall->tag); + switch (rcall->id) { default: eprintk(KERN_ERR, "unknown message type: %d\n", rcall->id); return -EPROTO; case RVERSION: rcall->params.rversion.msize = buf_get_int32(bufp); - rcall->params.rversion.version = buf_get_stringb(bufp, dbufp); + buf_get_str(bufp, &rcall->params.rversion.version); break; case RFLUSH: break; @@ -647,40 +381,27 @@ v9fs_deserialize_fcall(void *buf, u32 buflen, struct v9fs_fcall *rcall, break; case RWALK: rcall->params.rwalk.nwqid = buf_get_int16(bufp); - if (rcall->params.rwalk.nwqid > 16) { - eprintk(KERN_ERR, "Rwalk with more than 16 qids: %d\n", - rcall->params.rwalk.nwqid); + if (rcall->params.rwalk.nwqid > V9FS_MAXWELEM) { + eprintk(KERN_ERR, "Rwalk with more than %d qids: %d\n", + V9FS_MAXWELEM, rcall->params.rwalk.nwqid); return -EPROTO; } - rcall->params.rwalk.wqids = buf_alloc(dbufp, - rcall->params.rwalk.nwqid * sizeof(struct v9fs_qid)); - if (rcall->params.rwalk.wqids) - for (i = 0; i < rcall->params.rwalk.nwqid; i++) { - rcall->params.rwalk.wqids[i].type = - buf_get_int8(bufp); - rcall->params.rwalk.wqids[i].version = - buf_get_int16(bufp); - rcall->params.rwalk.wqids[i].path = - buf_get_int64(bufp); - } + for (i = 0; i < rcall->params.rwalk.nwqid; i++) + buf_get_qid(bufp, &rcall->params.rwalk.wqids[i]); break; case ROPEN: - rcall->params.ropen.qid.type = buf_get_int8(bufp); - rcall->params.ropen.qid.version = buf_get_int32(bufp); - rcall->params.ropen.qid.path = buf_get_int64(bufp); + buf_get_qid(bufp, &rcall->params.ropen.qid); rcall->params.ropen.iounit = buf_get_int32(bufp); break; case RCREATE: - rcall->params.rcreate.qid.type = buf_get_int8(bufp); - rcall->params.rcreate.qid.version = buf_get_int32(bufp); - rcall->params.rcreate.qid.path = buf_get_int64(bufp); + buf_get_qid(bufp, &rcall->params.rcreate.qid); rcall->params.rcreate.iounit = buf_get_int32(bufp); break; case RREAD: rcall->params.rread.count = buf_get_int32(bufp); - rcall->params.rread.data = buf_get_datab(bufp, dbufp, - rcall->params.rread.count); + rcall->params.rread.data = bufp->p; + buf_check_size(bufp, rcall->params.rread.count); break; case RWRITE: rcall->params.rwrite.count = buf_get_int32(bufp); @@ -691,22 +412,442 @@ v9fs_deserialize_fcall(void *buf, u32 buflen, struct v9fs_fcall *rcall, break; case RSTAT: buf_get_int16(bufp); - rcall->params.rstat.stat = - deserialize_statb(bufp, dbufp, extended); + buf_get_stat(bufp, &rcall->params.rstat.stat, extended); break; case RWSTAT: break; case RERROR: - rcall->params.rerror.error = buf_get_stringb(bufp, dbufp); + buf_get_str(bufp, &rcall->params.rerror.error); if (extended) rcall->params.rerror.errno = buf_get_int16(bufp); break; } - if (buf_check_overflow(bufp) || buf_check_overflow(dbufp)) { + if (buf_check_overflow(bufp)) { dprintk(DEBUG_ERROR, "buffer overflow\n"); return -EIO; } - return rcall->size; + return bufp->p - bufp->sp; +} + +static inline void v9fs_put_int8(struct cbuf *bufp, u8 val, u8 * p) +{ + *p = val; + buf_put_int8(bufp, val); +} + +static inline void v9fs_put_int16(struct cbuf *bufp, u16 val, u16 * p) +{ + *p = val; + buf_put_int16(bufp, val); +} + +static inline void v9fs_put_int32(struct cbuf *bufp, u32 val, u32 * p) +{ + *p = val; + buf_put_int32(bufp, val); +} + +static inline void v9fs_put_int64(struct cbuf *bufp, u64 val, u64 * p) +{ + *p = val; + buf_put_int64(bufp, val); +} + +static inline void +v9fs_put_str(struct cbuf *bufp, char *data, struct v9fs_str *str) +{ + if (data) { + str->len = strlen(data); + str->str = bufp->p; + } else { + str->len = 0; + str->str = NULL; + } + + buf_put_stringn(bufp, data, str->len); +} + +static inline int +v9fs_put_user_data(struct cbuf *bufp, const char __user * data, int count, + unsigned char **pdata) +{ + *pdata = buf_alloc(bufp, count); + return copy_from_user(*pdata, data, count); +} + +static void +v9fs_put_wstat(struct cbuf *bufp, struct v9fs_wstat *wstat, + struct v9fs_stat *stat, int statsz, int extended) +{ + v9fs_put_int16(bufp, statsz, &stat->size); + v9fs_put_int16(bufp, wstat->type, &stat->type); + v9fs_put_int32(bufp, wstat->dev, &stat->dev); + v9fs_put_int8(bufp, wstat->qid.type, &stat->qid.type); + v9fs_put_int32(bufp, wstat->qid.version, &stat->qid.version); + v9fs_put_int64(bufp, wstat->qid.path, &stat->qid.path); + v9fs_put_int32(bufp, wstat->mode, &stat->mode); + v9fs_put_int32(bufp, wstat->atime, &stat->atime); + v9fs_put_int32(bufp, wstat->mtime, &stat->mtime); + v9fs_put_int64(bufp, wstat->length, &stat->length); + + v9fs_put_str(bufp, wstat->name, &stat->name); + v9fs_put_str(bufp, wstat->uid, &stat->uid); + v9fs_put_str(bufp, wstat->gid, &stat->gid); + v9fs_put_str(bufp, wstat->muid, &stat->muid); + + if (extended) { + v9fs_put_str(bufp, wstat->extension, &stat->extension); + v9fs_put_int32(bufp, wstat->n_uid, &stat->n_uid); + v9fs_put_int32(bufp, wstat->n_gid, &stat->n_gid); + v9fs_put_int32(bufp, wstat->n_muid, &stat->n_muid); + } +} + +static struct v9fs_fcall * +v9fs_create_common(struct cbuf *bufp, u32 size, u8 id) +{ + struct v9fs_fcall *fc; + + size += 4 + 1 + 2; /* size[4] id[1] tag[2] */ + fc = kmalloc(sizeof(struct v9fs_fcall) + size, GFP_KERNEL); + if (!fc) + return ERR_PTR(-ENOMEM); + + fc->sdata = (char *)fc + sizeof(*fc); + + buf_init(bufp, (char *)fc->sdata, size); + v9fs_put_int32(bufp, size, &fc->size); + v9fs_put_int8(bufp, id, &fc->id); + v9fs_put_int16(bufp, V9FS_NOTAG, &fc->tag); + + return fc; +} + +void v9fs_set_tag(struct v9fs_fcall *fc, u16 tag) +{ + *(__le16 *) (fc->sdata + 5) = cpu_to_le16(tag); +} + +struct v9fs_fcall *v9fs_create_tversion(u32 msize, char *version) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 2 + strlen(version); /* msize[4] version[s] */ + fc = v9fs_create_common(bufp, size, TVERSION); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, msize, &fc->params.tversion.msize); + v9fs_put_str(bufp, version, &fc->params.tversion.version); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tauth(u32 afid, char *uname, char *aname) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 2 + strlen(uname) + 2 + strlen(aname); /* afid[4] uname[s] aname[s] */ + fc = v9fs_create_common(bufp, size, TAUTH); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, afid, &fc->params.tauth.afid); + v9fs_put_str(bufp, uname, &fc->params.tauth.uname); + v9fs_put_str(bufp, aname, &fc->params.tauth.aname); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall * +v9fs_create_tattach(u32 fid, u32 afid, char *uname, char *aname) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 4 + 2 + strlen(uname) + 2 + strlen(aname); /* fid[4] afid[4] uname[s] aname[s] */ + fc = v9fs_create_common(bufp, size, TATTACH); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.tattach.fid); + v9fs_put_int32(bufp, afid, &fc->params.tattach.afid); + v9fs_put_str(bufp, uname, &fc->params.tattach.uname); + v9fs_put_str(bufp, aname, &fc->params.tattach.aname); + + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tflush(u16 oldtag) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 2; /* oldtag[2] */ + fc = v9fs_create_common(bufp, size, TFLUSH); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int16(bufp, oldtag, &fc->params.tflush.oldtag); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_twalk(u32 fid, u32 newfid, u16 nwname, + char **wnames) +{ + int i, size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + if (nwname > V9FS_MAXWELEM) { + dprintk(DEBUG_ERROR, "nwname > %d\n", V9FS_MAXWELEM); + return NULL; + } + + size = 4 + 4 + 2; /* fid[4] newfid[4] nwname[2] ... */ + for (i = 0; i < nwname; i++) { + size += 2 + strlen(wnames[i]); /* wname[s] */ + } + + fc = v9fs_create_common(bufp, size, TWALK); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.twalk.fid); + v9fs_put_int32(bufp, newfid, &fc->params.twalk.newfid); + v9fs_put_int16(bufp, nwname, &fc->params.twalk.nwname); + for (i = 0; i < nwname; i++) { + v9fs_put_str(bufp, wnames[i], &fc->params.twalk.wnames[i]); + } + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_topen(u32 fid, u8 mode) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 1; /* fid[4] mode[1] */ + fc = v9fs_create_common(bufp, size, TOPEN); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.topen.fid); + v9fs_put_int8(bufp, mode, &fc->params.topen.mode); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tcreate(u32 fid, char *name, u32 perm, u8 mode) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 2 + strlen(name) + 4 + 1; /* fid[4] name[s] perm[4] mode[1] */ + fc = v9fs_create_common(bufp, size, TCREATE); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.tcreate.fid); + v9fs_put_str(bufp, name, &fc->params.tcreate.name); + v9fs_put_int32(bufp, perm, &fc->params.tcreate.perm); + v9fs_put_int8(bufp, mode, &fc->params.tcreate.mode); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tread(u32 fid, u64 offset, u32 count) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 8 + 4; /* fid[4] offset[8] count[4] */ + fc = v9fs_create_common(bufp, size, TREAD); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.tread.fid); + v9fs_put_int64(bufp, offset, &fc->params.tread.offset); + v9fs_put_int32(bufp, count, &fc->params.tread.count); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_twrite(u32 fid, u64 offset, u32 count, + const char __user * data) +{ + int size, err; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4 + 8 + 4 + count; /* fid[4] offset[8] count[4] data[count] */ + fc = v9fs_create_common(bufp, size, TWRITE); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.twrite.fid); + v9fs_put_int64(bufp, offset, &fc->params.twrite.offset); + v9fs_put_int32(bufp, count, &fc->params.twrite.count); + err = v9fs_put_user_data(bufp, data, count, &fc->params.twrite.data); + if (err) { + kfree(fc); + fc = ERR_PTR(err); + } + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tclunk(u32 fid) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4; /* fid[4] */ + fc = v9fs_create_common(bufp, size, TCLUNK); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.tclunk.fid); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tremove(u32 fid) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4; /* fid[4] */ + fc = v9fs_create_common(bufp, size, TREMOVE); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.tremove.fid); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_tstat(u32 fid) +{ + int size; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + size = 4; /* fid[4] */ + fc = v9fs_create_common(bufp, size, TSTAT); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.tstat.fid); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; +} + +struct v9fs_fcall *v9fs_create_twstat(u32 fid, struct v9fs_wstat *wstat, + int extended) +{ + int size, statsz; + struct v9fs_fcall *fc; + struct cbuf buffer; + struct cbuf *bufp = &buffer; + + statsz = v9fs_size_wstat(wstat, extended); + size = 4 + 2 + 2 + statsz; /* fid[4] stat[n] */ + fc = v9fs_create_common(bufp, size, TWSTAT); + if (IS_ERR(fc)) + goto error; + + v9fs_put_int32(bufp, fid, &fc->params.twstat.fid); + buf_put_int16(bufp, statsz + 2); + v9fs_put_wstat(bufp, wstat, &fc->params.twstat.stat, statsz, extended); + + if (buf_check_overflow(bufp)) { + kfree(fc); + fc = ERR_PTR(-ENOMEM); + } + error: + return fc; } diff --git a/fs/9p/conv.h b/fs/9p/conv.h index d5e33e17a68..26a736e4a2e 100644 --- a/fs/9p/conv.h +++ b/fs/9p/conv.h @@ -1,8 +1,9 @@ /* * linux/fs/9p/conv.h * - * 9P protocol conversion definitions + * 9P protocol conversion definitions. * + * Copyright (C) 2005 by Latchesar Ionkov * Copyright (C) 2004 by Eric Van Hensbergen * Copyright (C) 2002 by Ron Minnich * @@ -25,11 +26,26 @@ */ int v9fs_deserialize_stat(void *buf, u32 buflen, struct v9fs_stat *stat, - u32 statlen, int extended); -int v9fs_serialize_fcall(struct v9fs_fcall *tcall, void *buf, u32 buflen, int extended); int v9fs_deserialize_fcall(void *buf, u32 buflen, struct v9fs_fcall *rcall, - int rcalllen, int extended); + int extended); + +void v9fs_set_tag(struct v9fs_fcall *fc, u16 tag); -/* this one is actually in error.c right now */ -int v9fs_errstr2errno(char *errstr); +struct v9fs_fcall *v9fs_create_tversion(u32 msize, char *version); +struct v9fs_fcall *v9fs_create_tauth(u32 afid, char *uname, char *aname); +struct v9fs_fcall *v9fs_create_tattach(u32 fid, u32 afid, char *uname, + char *aname); +struct v9fs_fcall *v9fs_create_tflush(u16 oldtag); +struct v9fs_fcall *v9fs_create_twalk(u32 fid, u32 newfid, u16 nwname, + char **wnames); +struct v9fs_fcall *v9fs_create_topen(u32 fid, u8 mode); +struct v9fs_fcall *v9fs_create_tcreate(u32 fid, char *name, u32 perm, u8 mode); +struct v9fs_fcall *v9fs_create_tread(u32 fid, u64 offset, u32 count); +struct v9fs_fcall *v9fs_create_twrite(u32 fid, u64 offset, u32 count, + const char __user *data); +struct v9fs_fcall *v9fs_create_tclunk(u32 fid); +struct v9fs_fcall *v9fs_create_tremove(u32 fid); +struct v9fs_fcall *v9fs_create_tstat(u32 fid); +struct v9fs_fcall *v9fs_create_twstat(u32 fid, struct v9fs_wstat *wstat, + int extended); diff --git a/fs/9p/debug.h b/fs/9p/debug.h index 4445f06919d..fe551032788 100644 --- a/fs/9p/debug.h +++ b/fs/9p/debug.h @@ -51,16 +51,23 @@ do { \ #if DEBUG_DUMP_PKT static inline void dump_data(const unsigned char *data, unsigned int datalen) { - int i, j; - int len = datalen; + int i, n; + char buf[5*8]; - printk(KERN_DEBUG "data "); - for (i = 0; i < len; i += 4) { - for (j = 0; (j < 4) && (i + j < len); j++) - printk(KERN_DEBUG "%02x", data[i + j]); - printk(KERN_DEBUG " "); + n = 0; + i = 0; + while (i < datalen) { + n += snprintf(buf+n, sizeof(buf)-n, "%02x", data[i++]); + if (i%4 == 0) + n += snprintf(buf+n, sizeof(buf)-n, " "); + + if (i%16 == 0) { + dprintk(DEBUG_ERROR, "%s\n", buf); + n = 0; + } } - printk(KERN_DEBUG "\n"); + + dprintk(DEBUG_ERROR, "%s\n", buf); } #else /* DEBUG_DUMP_PKT */ static inline void dump_data(const unsigned char *data, unsigned int datalen) diff --git a/fs/9p/error.c b/fs/9p/error.c index 834cb179e38..e4b6f8f38b6 100644 --- a/fs/9p/error.c +++ b/fs/9p/error.c @@ -33,7 +33,6 @@ #include #include -#include #include "debug.h" #include "error.h" @@ -55,7 +54,8 @@ int v9fs_error_init(void) /* load initial error map into hash table */ for (c = errmap; c->name != NULL; c++) { - bucket = jhash(c->name, strlen(c->name), 0) % ERRHASHSZ; + c->namelen = strlen(c->name); + bucket = jhash(c->name, c->namelen, 0) % ERRHASHSZ; INIT_HLIST_NODE(&c->list); hlist_add_head(&c->list, &hash_errmap[bucket]); } @@ -69,15 +69,15 @@ int v9fs_error_init(void) * */ -int v9fs_errstr2errno(char *errstr) +int v9fs_errstr2errno(char *errstr, int len) { int errno = 0; struct hlist_node *p = NULL; struct errormap *c = NULL; - int bucket = jhash(errstr, strlen(errstr), 0) % ERRHASHSZ; + int bucket = jhash(errstr, len, 0) % ERRHASHSZ; hlist_for_each_entry(c, p, &hash_errmap[bucket], list) { - if (!strcmp(c->name, errstr)) { + if (c->namelen==len && !memcmp(c->name, errstr, len)) { errno = c->val; break; } diff --git a/fs/9p/error.h b/fs/9p/error.h index 78f89acf7c9..8b3176b3f65 100644 --- a/fs/9p/error.h +++ b/fs/9p/error.h @@ -36,6 +36,7 @@ struct errormap { char *name; int val; + int namelen; struct hlist_node list; }; @@ -175,4 +176,4 @@ static struct errormap errmap[] = { }; extern int v9fs_error_init(void); -extern int v9fs_errstr2errno(char *errstr); +extern int v9fs_errstr2errno(char *errstr, int len); diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 60ef8aba757..eda449778fa 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -31,9 +31,6 @@ #include "v9fs.h" #include "9p.h" #include "v9fs_vfs.h" -#include "transport.h" -#include "mux.h" -#include "conv.h" #include "fid.h" /** diff --git a/fs/9p/mux.c b/fs/9p/mux.c index 62b6ad0767e..f21cf508397 100644 --- a/fs/9p/mux.c +++ b/fs/9p/mux.c @@ -35,8 +35,8 @@ #include "debug.h" #include "v9fs.h" #include "9p.h" -#include "transport.h" #include "conv.h" +#include "transport.h" #include "mux.h" #define ERREQFLUSH 1 @@ -74,6 +74,7 @@ struct v9fs_mux_data { wait_queue_head_t equeue; struct list_head req_list; struct list_head unsent_req_list; + struct v9fs_fcall *rcall; int rpos; char *rbuf; int wpos; @@ -101,11 +102,15 @@ struct v9fs_mux_rpc { wait_queue_head_t wqueue; }; +extern int v9fs_errstr2errno(char *str, int len); + static int v9fs_poll_proc(void *); static void v9fs_read_work(void *); static void v9fs_write_work(void *); static void v9fs_pollwait(struct file *filp, wait_queue_head_t * wait_address, poll_table * p); +static u16 v9fs_mux_get_tag(struct v9fs_mux_data *); +static void v9fs_mux_put_tag(struct v9fs_mux_data *, u16); static DECLARE_MUTEX(v9fs_mux_task_lock); static struct workqueue_struct *v9fs_mux_wq; @@ -166,8 +171,9 @@ static void v9fs_mux_poll_start(struct v9fs_mux_data *m) if (v9fs_mux_poll_tasks[i].task == NULL) { vpt = &v9fs_mux_poll_tasks[i]; dprintk(DEBUG_MUX, "create proc %p\n", vpt); - vpt->task = kthread_create(v9fs_poll_proc, - vpt, "v9fs-poll"); + vpt->task = + kthread_create(v9fs_poll_proc, vpt, + "v9fs-poll"); INIT_LIST_HEAD(&vpt->mux_list); vpt->muxnum = 0; v9fs_mux_poll_task_num++; @@ -253,7 +259,7 @@ struct v9fs_mux_data *v9fs_mux_init(struct v9fs_transport *trans, int msize, struct v9fs_mux_data *m, *mtmp; dprintk(DEBUG_MUX, "transport %p msize %d\n", trans, msize); - m = kmalloc(sizeof(struct v9fs_mux_data) + 2 * msize, GFP_KERNEL); + m = kmalloc(sizeof(struct v9fs_mux_data), GFP_KERNEL); if (!m) return ERR_PTR(-ENOMEM); @@ -268,10 +274,11 @@ struct v9fs_mux_data *v9fs_mux_init(struct v9fs_transport *trans, int msize, init_waitqueue_head(&m->equeue); INIT_LIST_HEAD(&m->req_list); INIT_LIST_HEAD(&m->unsent_req_list); + m->rcall = NULL; m->rpos = 0; - m->rbuf = (char *)m + sizeof(struct v9fs_mux_data); + m->rbuf = NULL; m->wpos = m->wsize = 0; - m->wbuf = m->rbuf + msize; + m->wbuf = NULL; INIT_WORK(&m->rq, v9fs_read_work, m); INIT_WORK(&m->wq, v9fs_write_work, m); m->wsched = 0; @@ -427,29 +434,6 @@ static int v9fs_poll_proc(void *a) return 0; } -static inline int v9fs_write_req(struct v9fs_mux_data *m, struct v9fs_req *req) -{ - int n; - - list_move_tail(&req->req_list, &m->req_list); - n = v9fs_serialize_fcall(req->tcall, m->wbuf, m->msize, *m->extended); - if (n < 0) { - req->err = n; - list_del(&req->req_list); - if (req->cb) { - spin_unlock(&m->lock); - (*req->cb) (req->cba, req->tcall, req->rcall, req->err); - req->cb = NULL; - spin_lock(&m->lock); - } else - kfree(req->rcall); - - kfree(req); - } - - return n; -} - /** * v9fs_write_work - called when a transport can send some data */ @@ -457,7 +441,7 @@ static void v9fs_write_work(void *a) { int n, err; struct v9fs_mux_data *m; - struct v9fs_req *req, *rtmp; + struct v9fs_req *req; m = a; @@ -472,17 +456,15 @@ static void v9fs_write_work(void *a) return; } - err = 0; spin_lock(&m->lock); - list_for_each_entry_safe(req, rtmp, &m->unsent_req_list, - req_list) { - err = v9fs_write_req(m, req); - if (err > 0) - break; - } - - m->wsize = err; + req = + list_entry(m->unsent_req_list.next, struct v9fs_req, + req_list); + list_move_tail(&req->req_list, &m->req_list); + m->wbuf = req->tcall->sdata; + m->wsize = req->tcall->size; m->wpos = 0; + dump_data(m->wbuf, m->wsize); spin_unlock(&m->lock); } @@ -526,24 +508,23 @@ static void v9fs_write_work(void *a) static void process_request(struct v9fs_mux_data *m, struct v9fs_req *req) { int ecode, tag; - char *ename; + struct v9fs_str *ename; tag = req->tag; if (req->rcall->id == RERROR && !req->err) { ecode = req->rcall->params.rerror.errno; - ename = req->rcall->params.rerror.error; + ename = &req->rcall->params.rerror.error; - dprintk(DEBUG_MUX, "Rerror %s\n", ename); + dprintk(DEBUG_MUX, "Rerror %.*s\n", ename->len, ename->str); if (*m->extended) req->err = -ecode; if (!req->err) { - req->err = v9fs_errstr2errno(ename); + req->err = v9fs_errstr2errno(ename->str, ename->len); if (!req->err) { /* string match failed */ - dprintk(DEBUG_ERROR, "unknown error: %s\n", - ename); + PRINT_FCALL_ERROR("unknown error", req->rcall); } if (!req->err) @@ -565,8 +546,7 @@ static void process_request(struct v9fs_mux_data *m, struct v9fs_req *req) } else kfree(req->rcall); - if (tag != V9FS_NOTAG) - v9fs_put_idpool(tag, &m->tidpool); + v9fs_mux_put_tag(m, tag); wake_up(&m->equeue); kfree(req); @@ -577,10 +557,11 @@ static void process_request(struct v9fs_mux_data *m, struct v9fs_req *req) */ static void v9fs_read_work(void *a) { - int n, err, rcallen; + int n, err; struct v9fs_mux_data *m; struct v9fs_req *req, *rptr, *rreq; struct v9fs_fcall *rcall; + char *rbuf; m = a; @@ -589,6 +570,19 @@ static void v9fs_read_work(void *a) rcall = NULL; dprintk(DEBUG_MUX, "start mux %p pos %d\n", m, m->rpos); + + if (!m->rcall) { + m->rcall = + kmalloc(sizeof(struct v9fs_fcall) + m->msize, GFP_KERNEL); + if (!m->rcall) { + err = -ENOMEM; + goto error; + } + + m->rbuf = (char *)m->rcall + sizeof(struct v9fs_fcall); + m->rpos = 0; + } + clear_bit(Rpending, &m->wsched); err = m->trans->read(m->trans, m->rbuf + m->rpos, m->msize - m->rpos); dprintk(DEBUG_MUX, "mux %p got %d bytes\n", m, err); @@ -613,21 +607,32 @@ static void v9fs_read_work(void *a) if (m->rpos < n) break; - rcallen = n + V9FS_FCALLHDRSZ; - rcall = kmalloc(rcallen, GFP_KERNEL); - if (!rcall) { - err = -ENOMEM; - goto error; - } - dump_data(m->rbuf, n); - err = v9fs_deserialize_fcall(m->rbuf, n, rcall, rcallen, - *m->extended); + err = + v9fs_deserialize_fcall(m->rbuf, n, m->rcall, *m->extended); if (err < 0) { - kfree(rcall); goto error; } + rcall = m->rcall; + rbuf = m->rbuf; + if (m->rpos > n) { + m->rcall = kmalloc(sizeof(struct v9fs_fcall) + m->msize, + GFP_KERNEL); + if (!m->rcall) { + err = -ENOMEM; + goto error; + } + + m->rbuf = (char *)m->rcall + sizeof(struct v9fs_fcall); + memmove(m->rbuf, rbuf + n, m->rpos - n); + m->rpos -= n; + } else { + m->rcall = NULL; + m->rbuf = NULL; + m->rpos = 0; + } + dprintk(DEBUG_MUX, "mux %p fcall id %d tag %d\n", m, rcall->id, rcall->tag); @@ -642,6 +647,7 @@ static void v9fs_read_work(void *a) process_request(m, req); break; } + } if (!req) { @@ -652,10 +658,6 @@ static void v9fs_read_work(void *a) m, rcall->id, rcall->tag); kfree(rcall); } - - if (m->rpos > n) - memmove(m->rbuf, m->rbuf + n, m->rpos - n); - m->rpos -= n; } if (!list_empty(&m->req_list)) { @@ -710,12 +712,13 @@ static struct v9fs_req *v9fs_send_request(struct v9fs_mux_data *m, if (tc->id == TVERSION) n = V9FS_NOTAG; else - n = v9fs_get_idpool(&m->tidpool); + n = v9fs_mux_get_tag(m); if (n < 0) return ERR_PTR(-ENOMEM); - tc->tag = n; + v9fs_set_tag(tc, n); + req->tag = n; req->tcall = tc; req->rcall = NULL; @@ -773,9 +776,7 @@ v9fs_mux_flush_cb(void *a, struct v9fs_fcall *tc, struct v9fs_fcall *rc, if (!cb) spin_unlock(&m->lock); - if (v9fs_check_idpool(tag, &m->tidpool)) - v9fs_put_idpool(tag, &m->tidpool); - + v9fs_mux_put_tag(m, tag); kfree(tc); kfree(rc); } @@ -787,10 +788,7 @@ v9fs_mux_flush_request(struct v9fs_mux_data *m, struct v9fs_req *req) dprintk(DEBUG_MUX, "mux %p req %p tag %d\n", m, req, req->tag); - fc = kmalloc(sizeof(struct v9fs_fcall), GFP_KERNEL); - fc->id = TFLUSH; - fc->params.tflush.oldtag = req->tag; - + fc = v9fs_create_tflush(req->tag); v9fs_send_request(m, fc, v9fs_mux_flush_cb, m); } @@ -939,3 +937,20 @@ void v9fs_mux_cancel(struct v9fs_mux_data *m, int err) wake_up(&m->equeue); } + +static u16 v9fs_mux_get_tag(struct v9fs_mux_data *m) +{ + int tag; + + tag = v9fs_get_idpool(&m->tidpool); + if (tag < 0) + return V9FS_NOTAG; + else + return (u16) tag; +} + +static void v9fs_mux_put_tag(struct v9fs_mux_data *m, u16 tag) +{ + if (tag != V9FS_NOTAG && v9fs_check_idpool(tag, &m->tidpool)) + v9fs_put_idpool(tag, &m->tidpool); +} diff --git a/fs/9p/trans_sock.c b/fs/9p/trans_sock.c index 9ef404c75c8..44e830697ac 100644 --- a/fs/9p/trans_sock.c +++ b/fs/9p/trans_sock.c @@ -110,7 +110,6 @@ static int v9fs_sock_send(struct v9fs_transport *trans, void *v, int len) if (!(ts->filp->f_flags & O_NONBLOCK)) dprintk(DEBUG_ERROR, "blocking write ...\n"); - dump_data(v, len); oldfs = get_fs(); set_fs(get_ds()); ret = vfs_write(ts->filp, (void __user *)v, len, &ts->filp->f_pos); diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 5e0f79355fd..519b21d8b15 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -37,7 +37,6 @@ #include "v9fs_vfs.h" #include "transport.h" #include "mux.h" -#include "conv.h" /* TODO: sysfs or debugfs interface */ int v9fs_debug_level = 0; /* feature-rific global debug level */ @@ -353,7 +352,7 @@ v9fs_session_init(struct v9fs_session_info *v9ses, } /* Really should check for 9P1 and report error */ - if (!strcmp(fcall->params.rversion.version, "9P2000.u")) { + if (!v9fs_str_compare("9P2000.u", &fcall->params.rversion.version)) { dprintk(DEBUG_9P, "9P2000 UNIX extensions enabled\n"); v9ses->extended = 1; } else { diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h index 2f2cea7ee3e..c78502ad00e 100644 --- a/fs/9p/v9fs_vfs.h +++ b/fs/9p/v9fs_vfs.h @@ -45,9 +45,8 @@ extern struct dentry_operations v9fs_dentry_operations; struct inode *v9fs_get_inode(struct super_block *sb, int mode); ino_t v9fs_qid2ino(struct v9fs_qid *qid); -void v9fs_mistat2inode(struct v9fs_stat *, struct inode *, - struct super_block *); +void v9fs_stat2inode(struct v9fs_stat *, struct inode *, struct super_block *); int v9fs_dir_release(struct inode *inode, struct file *filp); int v9fs_file_open(struct inode *inode, struct file *file); -void v9fs_inode2mistat(struct inode *inode, struct v9fs_stat *mistat); +void v9fs_inode2stat(struct inode *inode, struct v9fs_stat *stat); void v9fs_dentry_release(struct dentry *); diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c index 4887df76739..2dd806dac9f 100644 --- a/fs/9p/vfs_dentry.c +++ b/fs/9p/vfs_dentry.c @@ -40,7 +40,6 @@ #include "v9fs.h" #include "9p.h" #include "v9fs_vfs.h" -#include "conv.h" #include "fid.h" /** @@ -108,7 +107,8 @@ void v9fs_dentry_release(struct dentry *dentry) err = v9fs_t_clunk(current_fid->v9ses, current_fid->fid); if (err < 0) - dprintk(DEBUG_ERROR, "clunk failed: %d\n", err); + dprintk(DEBUG_ERROR, "clunk failed: %d name %s\n", + err, dentry->d_iname); v9fs_fid_destroy(current_fid); } diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index 3893dd307dd..ae6d032b9b5 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -37,8 +37,8 @@ #include "debug.h" #include "v9fs.h" #include "9p.h" -#include "v9fs_vfs.h" #include "conv.h" +#include "v9fs_vfs.h" #include "fid.h" /** @@ -77,17 +77,13 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) unsigned int i, n, s; int fid = -1; int ret = 0; - struct v9fs_stat *mi = NULL; + struct v9fs_stat stat; int over = 0; dprintk(DEBUG_VFS, "name %s\n", filp->f_dentry->d_name.name); fid = file->fid; - mi = kmalloc(v9ses->maxdata, GFP_KERNEL); - if (!mi) - return -ENOMEM; - if (file->rdir_fcall && (filp->f_pos != file->rdir_pos)) { kfree(file->rdir_fcall); file->rdir_fcall = NULL; @@ -99,18 +95,18 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) while (i < n) { s = v9fs_deserialize_stat( file->rdir_fcall->params.rread.data + i, - n - i, mi, v9ses->maxdata, v9ses->extended); + n - i, &stat, v9ses->extended); if (s == 0) { dprintk(DEBUG_ERROR, - "error while deserializing mistat\n"); + "error while deserializing stat\n"); ret = -EIO; goto FreeStructs; } - over = filldir(dirent, mi->name, strlen(mi->name), - filp->f_pos, v9fs_qid2ino(&mi->qid), - dt_type(mi)); + over = filldir(dirent, stat.name.str, stat.name.len, + filp->f_pos, v9fs_qid2ino(&stat.qid), + dt_type(&stat)); if (over) { file->rdir_fpos = i; @@ -130,7 +126,7 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) while (!over) { ret = v9fs_t_read(v9ses, fid, filp->f_pos, - v9ses->maxdata-V9FS_IOHDRSZ, &fcall); + v9ses->maxdata-V9FS_IOHDRSZ, &fcall); if (ret < 0) { dprintk(DEBUG_ERROR, "error while reading: %d: %p\n", ret, fcall); @@ -142,17 +138,17 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) i = 0; while (i < n) { s = v9fs_deserialize_stat(fcall->params.rread.data + i, - n - i, mi, v9ses->maxdata, v9ses->extended); + n - i, &stat, v9ses->extended); if (s == 0) { dprintk(DEBUG_ERROR, - "error while deserializing mistat\n"); + "error while deserializing stat\n"); return -EIO; } - over = filldir(dirent, mi->name, strlen(mi->name), - filp->f_pos, v9fs_qid2ino(&mi->qid), - dt_type(mi)); + over = filldir(dirent, stat.name.str, stat.name.len, + filp->f_pos, v9fs_qid2ino(&stat.qid), + dt_type(&stat)); if (over) { file->rdir_fcall = fcall; @@ -171,7 +167,6 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) FreeStructs: kfree(fcall); - kfree(mi); return ret; } diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index e13577da913..6852f0eb96e 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -117,9 +118,7 @@ int v9fs_file_open(struct inode *inode, struct file *file) result = v9fs_t_open(v9ses, newfid, open_mode, &fcall); if (result < 0) { - dprintk(DEBUG_ERROR, - "open failed, open_mode 0x%x: %s\n", open_mode, - FCALL_ERROR(fcall)); + PRINT_FCALL_ERROR("open failed", fcall); kfree(fcall); return result; } @@ -256,7 +255,6 @@ v9fs_file_write(struct file *filp, const char __user * data, int result = -EIO; int rsize = 0; int total = 0; - char *buf; dprintk(DEBUG_VFS, "data %p count %d offset %x\n", data, (int)count, (int)*offset); @@ -264,28 +262,14 @@ v9fs_file_write(struct file *filp, const char __user * data, if (v9fid->iounit != 0 && rsize > v9fid->iounit) rsize = v9fid->iounit; - buf = kmalloc(v9ses->maxdata - V9FS_IOHDRSZ, GFP_KERNEL); - if (!buf) - return -ENOMEM; - do { if (count < rsize) rsize = count; - result = copy_from_user(buf, data, rsize); - if (result) { - dprintk(DEBUG_ERROR, "Problem copying from user\n"); - kfree(buf); - return -EFAULT; - } - - dump_data(buf, rsize); - result = v9fs_t_write(v9ses, fid, *offset, rsize, buf, &fcall); + result = v9fs_t_write(v9ses, fid, *offset, rsize, data, &fcall); if (result < 0) { - eprintk(KERN_ERR, "error while writing: %s(%d)\n", - FCALL_ERROR(fcall), result); + PRINT_FCALL_ERROR("error while writing", fcall); kfree(fcall); - kfree(buf); return result; } else *offset += result; @@ -305,7 +289,6 @@ v9fs_file_write(struct file *filp, const char __user * data, total += result; } while (count); - kfree(buf); return total; } diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index f11edde6432..742bcd0dc4a 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -40,7 +40,6 @@ #include "v9fs.h" #include "9p.h" #include "v9fs_vfs.h" -#include "conv.h" #include "fid.h" static struct inode_operations v9fs_dir_inode_operations; @@ -127,100 +126,32 @@ static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode) } /** - * v9fs_blank_mistat - helper function to setup a 9P stat structure + * v9fs_blank_wstat - helper function to setup a 9P stat structure * @v9ses: 9P session info (for determining extended mode) - * @mistat: structure to initialize + * @wstat: structure to initialize * */ static void -v9fs_blank_mistat(struct v9fs_session_info *v9ses, struct v9fs_stat *mistat) +v9fs_blank_wstat(struct v9fs_wstat *wstat) { - mistat->type = ~0; - mistat->dev = ~0; - mistat->qid.type = ~0; - mistat->qid.version = ~0; - *((long long *)&mistat->qid.path) = ~0; - mistat->mode = ~0; - mistat->atime = ~0; - mistat->mtime = ~0; - mistat->length = ~0; - mistat->name = mistat->data; - mistat->uid = mistat->data; - mistat->gid = mistat->data; - mistat->muid = mistat->data; - if (v9ses->extended) { - mistat->n_uid = ~0; - mistat->n_gid = ~0; - mistat->n_muid = ~0; - mistat->extension = mistat->data; - } - *mistat->data = 0; -} - -/** - * v9fs_mistat2unix - convert mistat to unix stat - * @mistat: Plan 9 metadata (mistat) structure - * @buf: unix metadata (stat) structure to populate - * @sb: superblock - * - */ - -static void -v9fs_mistat2unix(struct v9fs_stat *mistat, struct stat *buf, - struct super_block *sb) -{ - struct v9fs_session_info *v9ses = sb ? sb->s_fs_info : NULL; - - buf->st_nlink = 1; - - buf->st_atime = mistat->atime; - buf->st_mtime = mistat->mtime; - buf->st_ctime = mistat->mtime; - - buf->st_uid = (unsigned short)-1; - buf->st_gid = (unsigned short)-1; - - if (v9ses && v9ses->extended) { - /* TODO: string to uid mapping via user-space daemon */ - if (mistat->n_uid != -1) - sscanf(mistat->uid, "%x", (unsigned int *)&buf->st_uid); - - if (mistat->n_gid != -1) - sscanf(mistat->gid, "%x", (unsigned int *)&buf->st_gid); - } - - if (buf->st_uid == (unsigned short)-1) - buf->st_uid = v9ses->uid; - if (buf->st_gid == (unsigned short)-1) - buf->st_gid = v9ses->gid; - - buf->st_mode = p9mode2unixmode(v9ses, mistat->mode); - if ((S_ISBLK(buf->st_mode)) || (S_ISCHR(buf->st_mode))) { - char type = 0; - int major = -1; - int minor = -1; - sscanf(mistat->extension, "%c %u %u", &type, &major, &minor); - switch (type) { - case 'c': - buf->st_mode &= ~S_IFBLK; - buf->st_mode |= S_IFCHR; - break; - case 'b': - break; - default: - dprintk(DEBUG_ERROR, "Unknown special type %c (%s)\n", - type, mistat->extension); - }; - buf->st_rdev = MKDEV(major, minor); - } else - buf->st_rdev = 0; - - buf->st_size = mistat->length; - - buf->st_blksize = sb->s_blocksize; - buf->st_blocks = - (buf->st_size + buf->st_blksize - 1) >> sb->s_blocksize_bits; + wstat->type = ~0; + wstat->dev = ~0; + wstat->qid.type = ~0; + wstat->qid.version = ~0; + *((long long *)&wstat->qid.path) = ~0; + wstat->mode = ~0; + wstat->atime = ~0; + wstat->mtime = ~0; + wstat->length = ~0; + wstat->name = NULL; + wstat->uid = NULL; + wstat->gid = NULL; + wstat->muid = NULL; + wstat->n_uid = ~0; + wstat->n_gid = ~0; + wstat->n_muid = ~0; + wstat->extension = NULL; } /** @@ -312,7 +243,6 @@ v9fs_create(struct inode *dir, struct inode *file_inode = NULL; struct v9fs_fcall *fcall = NULL; struct v9fs_qid qid; - struct stat newstat; int dirfidnum = -1; long newfid = -1; int result = 0; @@ -350,7 +280,7 @@ v9fs_create(struct inode *dir, result = v9fs_t_walk(v9ses, dirfidnum, newfid, NULL, &fcall); if (result < 0) { - dprintk(DEBUG_ERROR, "clone error: %s\n", FCALL_ERROR(fcall)); + PRINT_FCALL_ERROR("clone error", fcall); v9fs_put_idpool(newfid, &v9ses->fidpool); newfid = -1; goto CleanUpFid; @@ -362,9 +292,7 @@ v9fs_create(struct inode *dir, result = v9fs_t_create(v9ses, newfid, (char *)file_dentry->d_name.name, perm, open_mode, &fcall); if (result < 0) { - dprintk(DEBUG_ERROR, "create fails: %s(%d)\n", - FCALL_ERROR(fcall), result); - + PRINT_FCALL_ERROR("create fails", fcall); goto CleanUpFid; } @@ -400,7 +328,7 @@ v9fs_create(struct inode *dir, result = v9fs_t_walk(v9ses, dirfidnum, wfidno, (char *) file_dentry->d_name.name, &fcall); if (result < 0) { - dprintk(DEBUG_ERROR, "clone error: %s\n", FCALL_ERROR(fcall)); + PRINT_FCALL_ERROR("clone error", fcall); v9fs_put_idpool(wfidno, &v9ses->fidpool); wfidno = -1; goto CleanUpFid; @@ -421,21 +349,21 @@ v9fs_create(struct inode *dir, result = v9fs_t_stat(v9ses, wfidno, &fcall); if (result < 0) { - dprintk(DEBUG_ERROR, "stat error: %s(%d)\n", FCALL_ERROR(fcall), - result); + PRINT_FCALL_ERROR("stat error", fcall); goto CleanUpFid; } - v9fs_mistat2unix(fcall->params.rstat.stat, &newstat, sb); - file_inode = v9fs_get_inode(sb, newstat.st_mode); + file_inode = v9fs_get_inode(sb, + p9mode2unixmode(v9ses, fcall->params.rstat.stat.mode)); + if ((!file_inode) || IS_ERR(file_inode)) { dprintk(DEBUG_ERROR, "create inode failed\n"); result = -EBADF; goto CleanUpFid; } - v9fs_mistat2inode(fcall->params.rstat.stat, file_inode, sb); + v9fs_stat2inode(&fcall->params.rstat.stat, file_inode, sb); kfree(fcall); fcall = NULL; file_dentry->d_op = &v9fs_dentry_operations; @@ -500,10 +428,9 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) } result = v9fs_t_remove(v9ses, fid, &fcall); - if (result < 0) - dprintk(DEBUG_ERROR, "remove of file fails: %s(%d)\n", - FCALL_ERROR(fcall), result); - else { + if (result < 0) { + PRINT_FCALL_ERROR("remove fails", fcall); + } else { v9fs_put_idpool(fid, &v9ses->fidpool); v9fs_fid_destroy(v9fid); } @@ -558,7 +485,6 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, struct v9fs_fid *fid; struct inode *inode; struct v9fs_fcall *fcall = NULL; - struct stat newstat; int dirfidnum = -1; int newfid = -1; int result = 0; @@ -611,8 +537,8 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, goto FreeFcall; } - v9fs_mistat2unix(fcall->params.rstat.stat, &newstat, sb); - inode = v9fs_get_inode(sb, newstat.st_mode); + inode = v9fs_get_inode(sb, p9mode2unixmode(v9ses, + fcall->params.rstat.stat.mode)); if (IS_ERR(inode) && (PTR_ERR(inode) == -ENOSPC)) { eprintk(KERN_WARNING, "inode alloc failes, returns %ld\n", @@ -622,7 +548,7 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, goto FreeFcall; } - inode->i_ino = v9fs_qid2ino(&fcall->params.rstat.stat->qid); + inode->i_ino = v9fs_qid2ino(&fcall->params.rstat.stat.qid); fid = v9fs_fid_create(dentry, v9ses, newfid, 0); if (fid == NULL) { @@ -631,10 +557,10 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, goto FreeFcall; } - fid->qid = fcall->params.rstat.stat->qid; + fid->qid = fcall->params.rstat.stat.qid; dentry->d_op = &v9fs_dentry_operations; - v9fs_mistat2inode(fcall->params.rstat.stat, inode, inode->i_sb); + v9fs_stat2inode(&fcall->params.rstat.stat, inode, inode->i_sb); d_add(dentry, inode); kfree(fcall); @@ -690,7 +616,7 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, v9fs_fid_lookup(old_dentry->d_parent); struct v9fs_fid *newdirfid = v9fs_fid_lookup(new_dentry->d_parent); - struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); + struct v9fs_wstat wstat; struct v9fs_fcall *fcall = NULL; int fid = -1; int olddirfidnum = -1; @@ -699,9 +625,6 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, dprintk(DEBUG_VFS, "\n"); - if (!mistat) - return -ENOMEM; - if ((!oldfid) || (!olddirfid) || (!newdirfid)) { dprintk(DEBUG_ERROR, "problem with arguments\n"); return -EBADF; @@ -725,26 +648,15 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto FreeFcallnBail; } - v9fs_blank_mistat(v9ses, mistat); - - strcpy(mistat->data + 1, v9ses->name); - mistat->name = mistat->data + 1 + strlen(v9ses->name); - - if (new_dentry->d_name.len > - (v9ses->maxdata - strlen(v9ses->name) - sizeof(struct v9fs_stat))) { - dprintk(DEBUG_ERROR, "new name too long\n"); - goto FreeFcallnBail; - } + v9fs_blank_wstat(&wstat); + wstat.muid = v9ses->name; + wstat.name = (char *) new_dentry->d_name.name; - strcpy(mistat->name, new_dentry->d_name.name); - retval = v9fs_t_wstat(v9ses, fid, mistat, &fcall); + retval = v9fs_t_wstat(v9ses, fid, &wstat, &fcall); FreeFcallnBail: - kfree(mistat); - if (retval < 0) - dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", - FCALL_ERROR(fcall)); + PRINT_FCALL_ERROR("wstat error", fcall); kfree(fcall); return retval; @@ -779,7 +691,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, if (err < 0) dprintk(DEBUG_ERROR, "stat error\n"); else { - v9fs_mistat2inode(fcall->params.rstat.stat, dentry->d_inode, + v9fs_stat2inode(&fcall->params.rstat.stat, dentry->d_inode, dentry->d_inode->i_sb); generic_fillattr(dentry->d_inode, stat); } @@ -800,57 +712,44 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); struct v9fs_fid *fid = v9fs_fid_lookup(dentry); struct v9fs_fcall *fcall = NULL; - struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); + struct v9fs_wstat wstat; int res = -EPERM; dprintk(DEBUG_VFS, "\n"); - if (!mistat) - return -ENOMEM; - if (!fid) { dprintk(DEBUG_ERROR, "Couldn't find fid associated with dentry\n"); return -EBADF; } - v9fs_blank_mistat(v9ses, mistat); + v9fs_blank_wstat(&wstat); if (iattr->ia_valid & ATTR_MODE) - mistat->mode = unixmode2p9mode(v9ses, iattr->ia_mode); + wstat.mode = unixmode2p9mode(v9ses, iattr->ia_mode); if (iattr->ia_valid & ATTR_MTIME) - mistat->mtime = iattr->ia_mtime.tv_sec; + wstat.mtime = iattr->ia_mtime.tv_sec; if (iattr->ia_valid & ATTR_ATIME) - mistat->atime = iattr->ia_atime.tv_sec; + wstat.atime = iattr->ia_atime.tv_sec; if (iattr->ia_valid & ATTR_SIZE) - mistat->length = iattr->ia_size; + wstat.length = iattr->ia_size; if (v9ses->extended) { - char *ptr = mistat->data+1; + if (iattr->ia_valid & ATTR_UID) + wstat.n_uid = iattr->ia_uid; - if (iattr->ia_valid & ATTR_UID) { - mistat->uid = ptr; - ptr += 1+sprintf(ptr, "%08x", iattr->ia_uid); - mistat->n_uid = iattr->ia_uid; - } - - if (iattr->ia_valid & ATTR_GID) { - mistat->gid = ptr; - ptr += 1+sprintf(ptr, "%08x", iattr->ia_gid); - mistat->n_gid = iattr->ia_gid; - } + if (iattr->ia_valid & ATTR_GID) + wstat.n_gid = iattr->ia_gid; } - res = v9fs_t_wstat(v9ses, fid->fid, mistat, &fcall); + res = v9fs_t_wstat(v9ses, fid->fid, &wstat, &fcall); if (res < 0) - dprintk(DEBUG_ERROR, "wstat error: %s\n", FCALL_ERROR(fcall)); + PRINT_FCALL_ERROR("wstat error", fcall); - kfree(mistat); kfree(fcall); - if (res >= 0) res = inode_setattr(dentry->d_inode, iattr); @@ -858,51 +757,42 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) } /** - * v9fs_mistat2inode - populate an inode structure with mistat info - * @mistat: Plan 9 metadata (mistat) structure + * v9fs_stat2inode - populate an inode structure with mistat info + * @stat: Plan 9 metadata (mistat) structure * @inode: inode to populate * @sb: superblock of filesystem * */ void -v9fs_mistat2inode(struct v9fs_stat *mistat, struct inode *inode, - struct super_block *sb) +v9fs_stat2inode(struct v9fs_stat *stat, struct inode *inode, + struct super_block *sb) { + char ext[32]; struct v9fs_session_info *v9ses = sb->s_fs_info; inode->i_nlink = 1; - inode->i_atime.tv_sec = mistat->atime; - inode->i_mtime.tv_sec = mistat->mtime; - inode->i_ctime.tv_sec = mistat->mtime; + inode->i_atime.tv_sec = stat->atime; + inode->i_mtime.tv_sec = stat->mtime; + inode->i_ctime.tv_sec = stat->mtime; - inode->i_uid = -1; - inode->i_gid = -1; + inode->i_uid = v9ses->uid; + inode->i_gid = v9ses->gid; if (v9ses->extended) { - /* TODO: string to uid mapping via user-space daemon */ - inode->i_uid = mistat->n_uid; - inode->i_gid = mistat->n_gid; - - if (mistat->n_uid == -1) - sscanf(mistat->uid, "%x", &inode->i_uid); - - if (mistat->n_gid == -1) - sscanf(mistat->gid, "%x", &inode->i_gid); + inode->i_uid = stat->n_uid; + inode->i_gid = stat->n_gid; } - if (inode->i_uid == -1) - inode->i_uid = v9ses->uid; - if (inode->i_gid == -1) - inode->i_gid = v9ses->gid; - - inode->i_mode = p9mode2unixmode(v9ses, mistat->mode); + inode->i_mode = p9mode2unixmode(v9ses, stat->mode); if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) { char type = 0; int major = -1; int minor = -1; - sscanf(mistat->extension, "%c %u %u", &type, &major, &minor); + + v9fs_str_copy(ext, sizeof(ext), &stat->extension); + sscanf(ext, "%c %u %u", &type, &major, &minor); switch (type) { case 'c': inode->i_mode &= ~S_IFBLK; @@ -911,14 +801,14 @@ v9fs_mistat2inode(struct v9fs_stat *mistat, struct inode *inode, case 'b': break; default: - dprintk(DEBUG_ERROR, "Unknown special type %c (%s)\n", - type, mistat->extension); + dprintk(DEBUG_ERROR, "Unknown special type %c (%.*s)\n", + type, stat->extension.len, stat->extension.str); }; inode->i_rdev = MKDEV(major, minor); } else inode->i_rdev = 0; - inode->i_size = mistat->length; + inode->i_size = stat->length; inode->i_blksize = sb->s_blocksize; inode->i_blocks = @@ -945,72 +835,6 @@ ino_t v9fs_qid2ino(struct v9fs_qid *qid) return i; } -/** - * v9fs_vfs_symlink - helper function to create symlinks - * @dir: directory inode containing symlink - * @dentry: dentry for symlink - * @symname: symlink data - * - * See 9P2000.u RFC for more information - * - */ - -static int -v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) -{ - int retval = -EPERM; - struct v9fs_fid *newfid; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); - struct v9fs_fcall *fcall = NULL; - struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); - int err; - - dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, - symname); - - if (!mistat) - return -ENOMEM; - - if (!v9ses->extended) { - dprintk(DEBUG_ERROR, "not extended\n"); - goto FreeFcall; - } - - /* issue a create */ - retval = v9fs_create(dir, dentry, S_IFLNK, 0); - if (retval != 0) - goto FreeFcall; - - newfid = v9fs_fid_lookup(dentry); - - /* issue a twstat */ - v9fs_blank_mistat(v9ses, mistat); - strcpy(mistat->data + 1, symname); - mistat->extension = mistat->data + 1; - retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall); - if (retval < 0) { - dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", - FCALL_ERROR(fcall)); - goto FreeFcall; - } - - kfree(fcall); - - err = v9fs_t_clunk(v9ses, newfid->fid); - if (err < 0) { - dprintk(DEBUG_ERROR, "clunk for symlink failed: %d\n", err); - goto FreeFcall; - } - - d_drop(dentry); /* FID - will this also clunk? */ - - FreeFcall: - kfree(mistat); - kfree(fcall); - - return retval; -} - /** * v9fs_readlink - read a symlink's location (internal version) * @dentry: dentry for symlink @@ -1050,16 +874,17 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) if (!fcall) return -EIO; - if (!(fcall->params.rstat.stat->mode & V9FS_DMSYMLINK)) { + if (!(fcall->params.rstat.stat.mode & V9FS_DMSYMLINK)) { retval = -EINVAL; goto FreeFcall; } /* copy extension buffer into buffer */ - if (strlen(fcall->params.rstat.stat->extension) < buflen) - buflen = strlen(fcall->params.rstat.stat->extension); + if (fcall->params.rstat.stat.extension.len < buflen) + buflen = fcall->params.rstat.stat.extension.len; - memcpy(buffer, fcall->params.rstat.stat->extension, buflen + 1); + memcpy(buffer, fcall->params.rstat.stat.extension.str, buflen - 1); + buffer[buflen-1] = 0; retval = buflen; @@ -1149,82 +974,111 @@ static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void __putname(s); } -/** - * v9fs_vfs_link - create a hardlink - * @old_dentry: dentry for file to link to - * @dir: inode destination for new link - * @dentry: dentry for link - * - */ - -/* XXX - lots of code dup'd from symlink and creates, - * figure out a better reuse strategy - */ - -static int -v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *dentry) +static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry, + int mode, const char *extension) { - int retval = -EPERM; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); - struct v9fs_fcall *fcall = NULL; - struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); - struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry); - struct v9fs_fid *newfid = NULL; - char *symname = __getname(); - int err; + int err, retval; + struct v9fs_session_info *v9ses; + struct v9fs_fcall *fcall; + struct v9fs_fid *fid; + struct v9fs_wstat wstat; - dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, - old_dentry->d_name.name); + v9ses = v9fs_inode2v9ses(dir); + retval = -EPERM; + fcall = NULL; if (!v9ses->extended) { dprintk(DEBUG_ERROR, "not extended\n"); - goto FreeMem; + goto free_mem; } - /* get fid of old_dentry */ - sprintf(symname, "hardlink(%d)\n", oldfid->fid); - /* issue a create */ - retval = v9fs_create(dir, dentry, V9FS_DMLINK, 0); + retval = v9fs_create(dir, dentry, mode, 0); if (retval != 0) - goto FreeMem; + goto free_mem; - newfid = v9fs_fid_lookup(dentry); - if (!newfid) { + fid = v9fs_fid_get_created(dentry); + if (!fid) { dprintk(DEBUG_ERROR, "couldn't resolve fid from dentry\n"); - goto FreeMem; + goto free_mem; } - /* issue a twstat */ - v9fs_blank_mistat(v9ses, mistat); - strcpy(mistat->data + 1, symname); - mistat->extension = mistat->data + 1; - retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall); + /* issue a Twstat */ + v9fs_blank_wstat(&wstat); + wstat.muid = v9ses->name; + wstat.extension = (char *) extension; + retval = v9fs_t_wstat(v9ses, fid->fid, &wstat, &fcall); if (retval < 0) { - dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", - FCALL_ERROR(fcall)); - goto FreeMem; + PRINT_FCALL_ERROR("wstat error", fcall); + goto free_mem; } - kfree(fcall); - - err = v9fs_t_clunk(v9ses, newfid->fid); - + err = v9fs_t_clunk(v9ses, fid->fid); if (err < 0) { - dprintk(DEBUG_ERROR, "clunk for symlink failed: %d\n", err); - goto FreeMem; + dprintk(DEBUG_ERROR, "clunk failed: %d\n", err); + goto free_mem; } d_drop(dentry); /* FID - will this also clunk? */ +free_mem: kfree(fcall); - fcall = NULL; + return retval; +} + +/** + * v9fs_vfs_symlink - helper function to create symlinks + * @dir: directory inode containing symlink + * @dentry: dentry for symlink + * @symname: symlink data + * + * See 9P2000.u RFC for more information + * + */ + +static int +v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) +{ + dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, + symname); + + return v9fs_vfs_mkspecial(dir, dentry, S_IFLNK, symname); +} + +/** + * v9fs_vfs_link - create a hardlink + * @old_dentry: dentry for file to link to + * @dir: inode destination for new link + * @dentry: dentry for link + * + */ + +/* XXX - lots of code dup'd from symlink and creates, + * figure out a better reuse strategy + */ + +static int +v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + int retval; + struct v9fs_fid *oldfid; + char *name; + + dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, + old_dentry->d_name.name); + + oldfid = v9fs_fid_lookup(old_dentry); + if (!oldfid) { + dprintk(DEBUG_ERROR, "can't find oldfid\n"); + return -EPERM; + } + + name = __getname(); + sprintf(name, "hardlink(%d)\n", oldfid->fid); + retval = v9fs_vfs_mkspecial(dir, dentry, V9FS_DMLINK, name); + __putname(name); - FreeMem: - kfree(mistat); - kfree(fcall); - __putname(symname); return retval; } @@ -1240,83 +1094,30 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, static int v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { - int retval = -EPERM; - struct v9fs_fid *newfid; - struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); - struct v9fs_fcall *fcall = NULL; - struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL); - char *symname = __getname(); - int err; + int retval; + char *name; dprintk(DEBUG_VFS, " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino, dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev)); - if (!mistat) - return -ENOMEM; - - if (!new_valid_dev(rdev)) { - retval = -EINVAL; - goto FreeMem; - } - - if (!v9ses->extended) { - dprintk(DEBUG_ERROR, "not extended\n"); - goto FreeMem; - } - - /* issue a create */ - retval = v9fs_create(dir, dentry, mode, 0); - - if (retval != 0) - goto FreeMem; - - newfid = v9fs_fid_lookup(dentry); - if (!newfid) { - dprintk(DEBUG_ERROR, "coudn't resove fid from dentry\n"); - retval = -EINVAL; - goto FreeMem; - } + if (!new_valid_dev(rdev)) + return -EINVAL; + name = __getname(); /* build extension */ if (S_ISBLK(mode)) - sprintf(symname, "b %u %u", MAJOR(rdev), MINOR(rdev)); + sprintf(name, "b %u %u", MAJOR(rdev), MINOR(rdev)); else if (S_ISCHR(mode)) - sprintf(symname, "c %u %u", MAJOR(rdev), MINOR(rdev)); + sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev)); else if (S_ISFIFO(mode)) - ; /* DO NOTHING */ + *name = 0; else { - retval = -EINVAL; - goto FreeMem; + __putname(name); + return -EINVAL; } - if (!S_ISFIFO(mode)) { - /* issue a twstat */ - v9fs_blank_mistat(v9ses, mistat); - strcpy(mistat->data + 1, symname); - mistat->extension = mistat->data + 1; - retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall); - if (retval < 0) { - dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n", - FCALL_ERROR(fcall)); - goto FreeMem; - } - } - - /* need to update dcache so we show up */ - kfree(fcall); - - err = v9fs_t_clunk(v9ses, newfid->fid); - if (err < 0) { - dprintk(DEBUG_ERROR, "clunk for symlink failed: %d\n", err); - goto FreeMem; - } - - d_drop(dentry); /* FID - will this also clunk? */ - - FreeMem: - kfree(mistat); - kfree(fcall); - __putname(symname); + retval = v9fs_vfs_mkspecial(dir, dentry, mode, name); + __putname(name); return retval; } diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 83b6edd0988..d4d71a9ca57 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -44,7 +44,6 @@ #include "v9fs.h" #include "9p.h" #include "v9fs_vfs.h" -#include "conv.h" #include "fid.h" static void v9fs_clear_inode(struct inode *); @@ -123,10 +122,11 @@ static struct super_block *v9fs_get_sb(struct file_system_type dprintk(DEBUG_VFS, " \n"); - v9ses = kcalloc(1, sizeof(struct v9fs_session_info), GFP_KERNEL); + v9ses = kmalloc(sizeof(struct v9fs_session_info), GFP_KERNEL); if (!v9ses) return ERR_PTR(-ENOMEM); + memset(v9ses, 0, sizeof(struct v9fs_session_info)); if ((newfid = v9fs_session_init(v9ses, dev_name, data)) < 0) { dprintk(DEBUG_ERROR, "problem initiating session\n"); kfree(v9ses); @@ -168,10 +168,10 @@ static struct super_block *v9fs_get_sb(struct file_system_type goto put_back_sb; } - root_fid->qid = fcall->params.rstat.stat->qid; + root_fid->qid = fcall->params.rstat.stat.qid; root->d_inode->i_ino = - v9fs_qid2ino(&fcall->params.rstat.stat->qid); - v9fs_mistat2inode(fcall->params.rstat.stat, root->d_inode, sb); + v9fs_qid2ino(&fcall->params.rstat.stat.qid); + v9fs_stat2inode(&fcall->params.rstat.stat, root->d_inode, sb); } kfree(fcall); -- cgit From 1dac06b20dcc8078dab037bd70652c69c67ba672 Mon Sep 17 00:00:00 2001 From: Latchesar Ionkov Date: Sun, 8 Jan 2006 01:05:02 -0800 Subject: [PATCH] v9fs: handle kthread_create failure, minor bugfixes - remove unnecessary -ENOMEM assignments - return correct value when buf_check_size for second time in a buffer - handle failures when create_workqueue and kthread_create are called - use kzalloc instead of kmalloc/memset 0 - v9fs_str_copy and v9fs_str_compare were buggy, were used only in one place, correct the logic and move it to the place it is used. Signed-off-by: Latchesar Ionkov Cc: Eric Van Hensbergen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/9p/9p.c | 10 ---------- fs/9p/9p.h | 3 --- fs/9p/conv.c | 45 +++++++++------------------------------------ fs/9p/error.h | 1 - fs/9p/mux.c | 38 ++++++++++++++++++++++++++------------ fs/9p/mux.h | 3 ++- fs/9p/v9fs.c | 19 ++++++++++++++----- fs/9p/vfs_inode.c | 7 ++++++- fs/9p/vfs_super.c | 3 +-- 9 files changed, 58 insertions(+), 71 deletions(-) (limited to 'fs') diff --git a/fs/9p/9p.c b/fs/9p/9p.c index dc3ce44ec83..1a6d08761f3 100644 --- a/fs/9p/9p.c +++ b/fs/9p/9p.c @@ -86,7 +86,6 @@ v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname, dprintk(DEBUG_9P, "uname '%s' aname '%s' fid %d afid %d\n", uname, aname, fid, afid); - ret = -ENOMEM; tc = v9fs_create_tattach(fid, afid, uname, aname); if (!IS_ERR(tc)) { ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); @@ -136,7 +135,6 @@ v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid) dprintk(DEBUG_9P, "fid %d\n", fid); - ret = -ENOMEM; rc = NULL; tc = v9fs_create_tclunk(fid); if (!IS_ERR(tc)) @@ -165,7 +163,6 @@ int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 oldtag) dprintk(DEBUG_9P, "oldtag %d\n", oldtag); - ret = -ENOMEM; tc = v9fs_create_tflush(oldtag); if (!IS_ERR(tc)) { ret = v9fs_mux_rpc(v9ses->mux, tc, NULL); @@ -221,7 +218,6 @@ v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid, dprintk(DEBUG_9P, "fid %d\n", fid); - ret = -ENOMEM; tc = v9fs_create_twstat(fid, wstat, v9ses->extended); if (!IS_ERR(tc)) { ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); @@ -259,7 +255,6 @@ v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid, else nwname = 0; - ret = -ENOMEM; tc = v9fs_create_twalk(fid, newfid, nwname, &name); if (!IS_ERR(tc)) { ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); @@ -289,7 +284,6 @@ v9fs_t_open(struct v9fs_session_info *v9ses, u32 fid, u8 mode, dprintk(DEBUG_9P, "fid %d mode %d\n", fid, mode); - ret = -ENOMEM; tc = v9fs_create_topen(fid, mode); if (!IS_ERR(tc)) { ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); @@ -317,7 +311,6 @@ v9fs_t_remove(struct v9fs_session_info *v9ses, u32 fid, dprintk(DEBUG_9P, "fid %d\n", fid); - ret = -ENOMEM; tc = v9fs_create_tremove(fid); if (!IS_ERR(tc)) { ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); @@ -349,7 +342,6 @@ v9fs_t_create(struct v9fs_session_info *v9ses, u32 fid, char *name, dprintk(DEBUG_9P, "fid %d name '%s' perm %x mode %d\n", fid, name, perm, mode); - ret = -ENOMEM; tc = v9fs_create_tcreate(fid, name, perm, mode); if (!IS_ERR(tc)) { ret = v9fs_mux_rpc(v9ses->mux, tc, rcp); @@ -380,7 +372,6 @@ v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, u64 offset, dprintk(DEBUG_9P, "fid %d offset 0x%llux count 0x%x\n", fid, (long long unsigned) offset, count); - ret = -ENOMEM; tc = v9fs_create_tread(fid, offset, count); if (!IS_ERR(tc)) { ret = v9fs_mux_rpc(v9ses->mux, tc, &rc); @@ -418,7 +409,6 @@ v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, u64 offset, u32 count, dprintk(DEBUG_9P, "fid %d offset 0x%llux count 0x%x\n", fid, (long long unsigned) offset, count); - ret = -ENOMEM; tc = v9fs_create_twrite(fid, offset, count, data); if (!IS_ERR(tc)) { ret = v9fs_mux_rpc(v9ses->mux, tc, &rc); diff --git a/fs/9p/9p.h b/fs/9p/9p.h index 007ff639777..0cd374d9471 100644 --- a/fs/9p/9p.h +++ b/fs/9p/9p.h @@ -340,9 +340,6 @@ struct v9fs_fcall { fcall?fcall->params.rerror.error.len:0, \ fcall?fcall->params.rerror.error.str:""); -char *v9fs_str_copy(char *buf, int buflen, struct v9fs_str *str); -int v9fs_str_compare(char *buf, struct v9fs_str *str); - int v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize, char *version, struct v9fs_fcall **rcall); diff --git a/fs/9p/conv.c b/fs/9p/conv.c index f62434d435b..55ccfa10ee9 100644 --- a/fs/9p/conv.c +++ b/fs/9p/conv.c @@ -45,37 +45,6 @@ struct cbuf { unsigned char *ep; }; -char *v9fs_str_copy(char *buf, int buflen, struct v9fs_str *str) -{ - int n; - - if (buflen < str->len) - n = buflen; - else - n = str->len; - - memmove(buf, str->str, n - 1); - - return buf; -} - -int v9fs_str_compare(char *buf, struct v9fs_str *str) -{ - int n, ret; - - ret = strncmp(buf, str->str, str->len); - - if (!ret) { - n = strlen(buf); - if (n < str->len) - ret = -1; - else if (n > str->len) - ret = 1; - } - - return ret; -} - static inline void buf_init(struct cbuf *buf, void *data, int datalen) { buf->sp = buf->p = data; @@ -89,11 +58,14 @@ static inline int buf_check_overflow(struct cbuf *buf) static inline int buf_check_size(struct cbuf *buf, int len) { - if (buf->p + len > buf->ep && buf->p < buf->ep) { - eprintk(KERN_ERR, "buffer overflow: want %d has %d\n", - len, (int)(buf->ep - buf->p)); - dump_stack(); - buf->p = buf->ep + 1; + if (buf->p + len > buf->ep) { + if (buf->p < buf->ep) { + eprintk(KERN_ERR, "buffer overflow: want %d has %d\n", + len, (int)(buf->ep - buf->p)); + dump_stack(); + buf->p = buf->ep + 1; + } + return 0; } @@ -527,6 +499,7 @@ v9fs_create_common(struct cbuf *bufp, u32 size, u8 id) void v9fs_set_tag(struct v9fs_fcall *fc, u16 tag) { + fc->tag = tag; *(__le16 *) (fc->sdata + 5) = cpu_to_le16(tag); } diff --git a/fs/9p/error.h b/fs/9p/error.h index 8b3176b3f65..a9794e85fe5 100644 --- a/fs/9p/error.h +++ b/fs/9p/error.h @@ -176,4 +176,3 @@ static struct errormap errmap[] = { }; extern int v9fs_error_init(void); -extern int v9fs_errstr2errno(char *errstr, int len); diff --git a/fs/9p/mux.c b/fs/9p/mux.c index f21cf508397..945cb368d45 100644 --- a/fs/9p/mux.c +++ b/fs/9p/mux.c @@ -102,8 +102,6 @@ struct v9fs_mux_rpc { wait_queue_head_t wqueue; }; -extern int v9fs_errstr2errno(char *str, int len); - static int v9fs_poll_proc(void *); static void v9fs_read_work(void *); static void v9fs_write_work(void *); @@ -119,7 +117,7 @@ static int v9fs_mux_num; static int v9fs_mux_poll_task_num; static struct v9fs_mux_poll_task v9fs_mux_poll_tasks[100]; -void v9fs_mux_global_init(void) +int v9fs_mux_global_init(void) { int i; @@ -127,6 +125,10 @@ void v9fs_mux_global_init(void) v9fs_mux_poll_tasks[i].task = NULL; v9fs_mux_wq = create_workqueue("v9fs"); + if (!v9fs_mux_wq) + return -ENOMEM; + + return 0; } void v9fs_mux_global_exit(void) @@ -156,10 +158,11 @@ inline int v9fs_mux_calc_poll_procs(int muxnum) return n; } -static void v9fs_mux_poll_start(struct v9fs_mux_data *m) +static int v9fs_mux_poll_start(struct v9fs_mux_data *m) { int i, n; struct v9fs_mux_poll_task *vpt, *vptlast; + struct task_struct *pproc; dprintk(DEBUG_MUX, "mux %p muxnum %d procnum %d\n", m, v9fs_mux_num, v9fs_mux_poll_task_num); @@ -171,13 +174,16 @@ static void v9fs_mux_poll_start(struct v9fs_mux_data *m) if (v9fs_mux_poll_tasks[i].task == NULL) { vpt = &v9fs_mux_poll_tasks[i]; dprintk(DEBUG_MUX, "create proc %p\n", vpt); - vpt->task = - kthread_create(v9fs_poll_proc, vpt, + pproc = kthread_create(v9fs_poll_proc, vpt, "v9fs-poll"); - INIT_LIST_HEAD(&vpt->mux_list); - vpt->muxnum = 0; - v9fs_mux_poll_task_num++; - wake_up_process(vpt->task); + + if (!IS_ERR(pproc)) { + vpt->task = pproc; + INIT_LIST_HEAD(&vpt->mux_list); + vpt->muxnum = 0; + v9fs_mux_poll_task_num++; + wake_up_process(vpt->task); + } break; } } @@ -207,16 +213,21 @@ static void v9fs_mux_poll_start(struct v9fs_mux_data *m) } if (i >= ARRAY_SIZE(v9fs_mux_poll_tasks)) { + if (vptlast == NULL) + return -ENOMEM; + dprintk(DEBUG_MUX, "put in proc %d\n", i); list_add(&m->mux_list, &vptlast->mux_list); vptlast->muxnum++; - m->poll_task = vpt; + m->poll_task = vptlast; memset(&m->poll_waddr, 0, sizeof(m->poll_waddr)); init_poll_funcptr(&m->pt, v9fs_pollwait); } v9fs_mux_num++; down(&v9fs_mux_task_lock); + + return 0; } static void v9fs_mux_poll_stop(struct v9fs_mux_data *m) @@ -283,7 +294,10 @@ struct v9fs_mux_data *v9fs_mux_init(struct v9fs_transport *trans, int msize, INIT_WORK(&m->wq, v9fs_write_work, m); m->wsched = 0; memset(&m->poll_waddr, 0, sizeof(m->poll_waddr)); - v9fs_mux_poll_start(m); + m->poll_task = NULL; + n = v9fs_mux_poll_start(m); + if (n) + return ERR_PTR(n); n = trans->poll(trans, &m->pt); if (n & POLLIN) { diff --git a/fs/9p/mux.h b/fs/9p/mux.h index 02b13b14b05..9473b84f24b 100644 --- a/fs/9p/mux.h +++ b/fs/9p/mux.h @@ -40,7 +40,7 @@ struct v9fs_mux_data; typedef void (*v9fs_mux_req_callback)(void *a, struct v9fs_fcall *tc, struct v9fs_fcall *rc, int err); -void v9fs_mux_global_init(void); +int v9fs_mux_global_init(void); void v9fs_mux_global_exit(void); struct v9fs_mux_data *v9fs_mux_init(struct v9fs_transport *trans, int msize, @@ -55,3 +55,4 @@ int v9fs_mux_rpcnb(struct v9fs_mux_data *m, struct v9fs_fcall *tc, void v9fs_mux_flush(struct v9fs_mux_data *m, int sendflush); void v9fs_mux_cancel(struct v9fs_mux_data *m, int err); +int v9fs_errstr2errno(char *errstr, int len); diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 519b21d8b15..5250c428fc1 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -269,6 +269,7 @@ v9fs_session_init(struct v9fs_session_info *v9ses, int n = 0; int newfid = -1; int retval = -EINVAL; + struct v9fs_str *version; v9ses->name = __getname(); if (!v9ses->name) @@ -351,13 +352,16 @@ v9fs_session_init(struct v9fs_session_info *v9ses, goto FreeFcall; } - /* Really should check for 9P1 and report error */ - if (!v9fs_str_compare("9P2000.u", &fcall->params.rversion.version)) { + version = &fcall->params.rversion.version; + if (version->len==8 && !memcmp(version->str, "9P2000.u", 8)) { dprintk(DEBUG_9P, "9P2000 UNIX extensions enabled\n"); v9ses->extended = 1; - } else { + } else if (version->len==6 && !memcmp(version->str, "9P2000", 6)) { dprintk(DEBUG_9P, "9P2000 legacy mode enabled\n"); v9ses->extended = 0; + } else { + retval = -EREMOTEIO; + goto FreeFcall; } n = fcall->params.rversion.msize; @@ -449,12 +453,17 @@ extern int v9fs_error_init(void); static int __init init_v9fs(void) { + int ret; + v9fs_error_init(); printk(KERN_INFO "Installing v9fs 9P2000 file system support\n"); - v9fs_mux_global_init(); - return register_filesystem(&v9fs_fs_type); + ret = v9fs_mux_global_init(); + if (!ret) + ret = register_filesystem(&v9fs_fs_type); + + return ret; } /** diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 742bcd0dc4a..d933ef1fbd8 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -768,6 +768,7 @@ void v9fs_stat2inode(struct v9fs_stat *stat, struct inode *inode, struct super_block *sb) { + int n; char ext[32]; struct v9fs_session_info *v9ses = sb->s_fs_info; @@ -791,7 +792,11 @@ v9fs_stat2inode(struct v9fs_stat *stat, struct inode *inode, int major = -1; int minor = -1; - v9fs_str_copy(ext, sizeof(ext), &stat->extension); + n = stat->extension.len; + if (n > sizeof(ext)-1) + n = sizeof(ext)-1; + memmove(ext, stat->extension.str, n); + ext[n] = 0; sscanf(ext, "%c %u %u", &type, &major, &minor); switch (type) { case 'c': diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index d4d71a9ca57..ae0f06b3c11 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -122,11 +122,10 @@ static struct super_block *v9fs_get_sb(struct file_system_type dprintk(DEBUG_VFS, " \n"); - v9ses = kmalloc(sizeof(struct v9fs_session_info), GFP_KERNEL); + v9ses = kzalloc(sizeof(struct v9fs_session_info), GFP_KERNEL); if (!v9ses) return ERR_PTR(-ENOMEM); - memset(v9ses, 0, sizeof(struct v9fs_session_info)); if ((newfid = v9fs_session_init(v9ses, dev_name, data)) < 0) { dprintk(DEBUG_ERROR, "problem initiating session\n"); kfree(v9ses); -- cgit From bfc090c468b33fb1f75c27a11efa7b7dc250556f Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Sun, 8 Jan 2006 01:05:08 -0800 Subject: [PATCH] afs: remove unnecessary __attribute__((packed)) Remove the unnecessary __attribute__((packed)) since the enum itself is packed and not the location of it in the structure. Signed-off-by: Jan Blunck Cc: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/afs/volume.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/afs/volume.h b/fs/afs/volume.h index 1e691889c4c..bfdcf19ba3f 100644 --- a/fs/afs/volume.h +++ b/fs/afs/volume.h @@ -18,8 +18,6 @@ #include "kafsasyncd.h" #include "cache.h" -#define __packed __attribute__((packed)) - typedef enum { AFS_VLUPD_SLEEP, /* sleeping waiting for update timer to fire */ AFS_VLUPD_PENDING, /* on pending queue */ @@ -115,7 +113,7 @@ struct afs_volume struct cachefs_cookie *cache; /* caching cookie */ #endif afs_volid_t vid; /* volume ID */ - afs_voltype_t __packed type; /* type of volume */ + afs_voltype_t type; /* type of volume */ char type_force; /* force volume type (suppress R/O -> R/W) */ unsigned short nservers; /* number of server slots filled */ unsigned short rjservers; /* number of servers discarded due to -ENOMEDIUM */ -- cgit From b01ec0ef63e95570e2463b26333d9c9c854cb941 Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sun, 8 Jan 2006 01:05:20 -0800 Subject: [PATCH] tiny: Uninline some open.c functions uninline some open.c functions add/remove: 3/0 grow/shrink: 0/6 up/down: 679/-1166 (-487) function old new delta do_sys_truncate - 336 +336 do_sys_ftruncate - 317 +317 __put_unused_fd - 26 +26 put_unused_fd 57 49 -8 sys_close 150 119 -31 sys_ftruncate64 260 26 -234 sys_ftruncate 272 24 -248 sys_truncate 339 25 -314 sys_truncate64 336 5 -331 Signed-off-by: Matt Mackall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/open.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/open.c b/fs/open.c index 94968cb3afc..75f3329e8a6 100644 --- a/fs/open.c +++ b/fs/open.c @@ -217,7 +217,7 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, return err; } -static inline long do_sys_truncate(const char __user * path, loff_t length) +static long do_sys_truncate(const char __user * path, loff_t length) { struct nameidata nd; struct inode * inode; @@ -283,7 +283,7 @@ asmlinkage long sys_truncate(const char __user * path, unsigned long length) return do_sys_truncate(path, (long)length); } -static inline long do_sys_ftruncate(unsigned int fd, loff_t length, int small) +static long do_sys_ftruncate(unsigned int fd, loff_t length, int small) { struct inode * inode; struct dentry *dentry; @@ -971,7 +971,7 @@ out: EXPORT_SYMBOL(get_unused_fd); -static inline void __put_unused_fd(struct files_struct *files, unsigned int fd) +static void __put_unused_fd(struct files_struct *files, unsigned int fd) { struct fdtable *fdt = files_fdtable(files); __FD_CLR(fd, fdt->open_fds); -- cgit From 5d2bea4582d20cb24085152acaa29b95c05cdcf8 Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sun, 8 Jan 2006 01:05:21 -0800 Subject: [PATCH] tiny: Uninline some inode.c functions uninline a couple inode.c functions add/remove: 2/0 grow/shrink: 0/5 up/down: 256/-428 (-172) function old new delta ifind - 136 +136 ifind_fast - 120 +120 ilookup5_nowait 131 80 -51 ilookup 158 71 -87 ilookup5 171 80 -91 iget_locked 190 95 -95 iget5_locked 240 136 -104 Signed-off-by: Matt Mackall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/inode.c b/fs/inode.c index d8d04bd72b5..fd568caf7f7 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -770,7 +770,7 @@ EXPORT_SYMBOL(igrab); * * Note, @test is called with the inode_lock held, so can't sleep. */ -static inline struct inode *ifind(struct super_block *sb, +static struct inode *ifind(struct super_block *sb, struct hlist_head *head, int (*test)(struct inode *, void *), void *data, const int wait) { @@ -804,7 +804,7 @@ static inline struct inode *ifind(struct super_block *sb, * * Otherwise NULL is returned. */ -static inline struct inode *ifind_fast(struct super_block *sb, +static struct inode *ifind_fast(struct super_block *sb, struct hlist_head *head, unsigned long ino) { struct inode *inode; -- cgit From 33443c42f4ffa5ca23b3323234bcb1a78e85d9db Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sun, 8 Jan 2006 01:05:22 -0800 Subject: [PATCH] tiny: Uninline some fslocks.c functions uninline some file locking functions add/remove: 3/0 grow/shrink: 0/15 up/down: 256/-1525 (-1269) function old new delta locks_free_lock - 134 +134 posix_same_owner - 69 +69 __locks_delete_block - 53 +53 posix_locks_conflict 126 108 -18 locks_remove_posix 266 237 -29 locks_wake_up_blocks 121 87 -34 locks_block_on_timeout 83 47 -36 locks_insert_block 157 120 -37 locks_delete_block 62 23 -39 posix_unblock_lock 104 59 -45 posix_locks_deadlock 162 100 -62 locks_delete_lock 228 119 -109 sys_flock 338 217 -121 __break_lease 600 474 -126 lease_init 252 122 -130 fcntl_setlk64 793 649 -144 fcntl_setlk 793 649 -144 __posix_lock_file 1477 1026 -451 Signed-off-by: Matt Mackall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/locks.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/locks.c b/fs/locks.c index fb32d6218e2..909eab8fb1d 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -154,7 +154,7 @@ static struct file_lock *locks_alloc_lock(void) } /* Free a lock which is not in use. */ -static inline void locks_free_lock(struct file_lock *fl) +static void locks_free_lock(struct file_lock *fl) { if (fl == NULL) { BUG(); @@ -475,8 +475,7 @@ static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2) /* * Check whether two locks have the same owner. */ -static inline int -posix_same_owner(struct file_lock *fl1, struct file_lock *fl2) +static int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2) { if (fl1->fl_lmops && fl1->fl_lmops->fl_compare_owner) return fl2->fl_lmops == fl1->fl_lmops && @@ -487,7 +486,7 @@ posix_same_owner(struct file_lock *fl1, struct file_lock *fl2) /* Remove waiter from blocker's block list. * When blocker ends up pointing to itself then the list is empty. */ -static inline void __locks_delete_block(struct file_lock *waiter) +static void __locks_delete_block(struct file_lock *waiter) { list_del_init(&waiter->fl_block); list_del_init(&waiter->fl_link); -- cgit From 708e9a794cf8822b760edaccd9053edb07c34d19 Mon Sep 17 00:00:00 2001 From: Matt Mackall Date: Sun, 8 Jan 2006 01:05:25 -0800 Subject: [PATCH] tiny: Configure ELF core dump support configurable support for ELF core dumps text data bss dec hex filename 3330172 529036 190556 4049764 3dcb64 vmlinux-baseline 3325552 528912 190556 4045020 3db8dc vmlinux-no-elf add/remove: 0/8 grow/shrink: 0/0 up/down: 0/-4424 (-4424) function old new delta fill_note 32 - -32 maydump 58 - -58 dump_seek 67 - -67 writenote 180 - -180 elf_dump_thread_status 274 - -274 fill_psinfo 308 - -308 fill_prstatus 466 - -466 elf_core_dump 3039 - -3039 Signed-off-by: Matt Mackall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/binfmt_elf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 288386b1def..80ca932ba0b 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -58,7 +58,7 @@ extern int dump_fpu (struct pt_regs *, elf_fpregset_t *); * If we don't support core dumping, then supply a NULL so we * don't even try. */ -#ifdef USE_ELF_CORE_DUMP +#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE) static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file); #else #define elf_core_dump NULL @@ -1113,7 +1113,7 @@ out: * Note that some platforms still use traditional core dumps and not * the ELF core dump. Each platform can select it as appropriate. */ -#ifdef USE_ELF_CORE_DUMP +#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE) /* * ELF core dumper -- cgit