--- linux-2.4.17/kernel/ksyms.c.lustre Fri Feb 22 15:26:38 2002 +++ linux-2.4.17/kernel/ksyms.c Fri Feb 22 15:27:44 2002 @@ -282,6 +282,11 @@ EXPORT_SYMBOL(lock_may_write); EXPORT_SYMBOL(dcache_readdir); +/* lustre */ +EXPORT_SYMBOL(pagecache_lock); +EXPORT_SYMBOL(do_kern_mount); + + /* for stackable file systems (lofs, wrapfs, cryptfs, etc.) */ EXPORT_SYMBOL(default_llseek); EXPORT_SYMBOL(dentry_open); --- linux-2.4.18/include/linux/errno.h Fri Feb 9 23:46:13 2001 +++ linux-2.4.18ea/include/linux/errno.h Sun Mar 24 23:39:31 2002 @@ -21,6 +21,10 @@ #define EBADTYPE 527 /* Type not supported by server */ #define EJUKEBOX 528 /* Request initiated, but will not complete before timeout */ +/* Defined for extended attributes */ +#define ENOATTR ENODATA /* No such attribute */ +#define ENOTSUP EOPNOTSUPP /* Operation not supported */ + #endif #endif --- linux-2.4.17/include/linux/fs.h.lustre Fri Feb 22 15:27:53 2002 +++ linux-2.4.17/include/linux/fs.h Fri Feb 22 15:28:52 2002 @@ -853,6 +865,10 @@ int (*revalidate) (struct dentry *); int (*setattr) (struct dentry *, struct iattr *); int (*getattr) (struct dentry *, struct iattr *); + int (*setxattr) (struct dentry *, const char *, void *, size_t, int); + ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); + ssize_t (*listxattr) (struct dentry *, char *, size_t); + int (*removexattr) (struct dentry *, const char *); }; struct seq_file; @@ -983,7 +984,7 @@ extern struct vfsmount *kern_mount(struct file_system_type *); extern int may_umount(struct vfsmount *); extern long do_mount(char *, char *, char *, unsigned long, void *); - +struct vfsmount *do_kern_mount(char *type, int flags, char *name, void *data); #define kern_umount mntput extern int vfs_statfs(struct super_block *, struct statfs *); diff -Nur linux-2.4.18/include/linux/limits.h linux-2.4.18ea/include/linux/limits.h --- linux-2.4.18/include/linux/limits.h Sun Feb 24 04:42:59 2002 +++ linux-2.4.18ea/include/linux/limits.h Sun Feb 24 04:34:48 2002 @@ -13,6 +13,9 @@ #define NAME_MAX 255 /* # chars in a file name */ #define PATH_MAX 4096 /* # chars in a path name including nul */ #define PIPE_BUF 4096 /* # bytes in atomic write to a pipe */ +#define XATTR_NAME_MAX 255 /* # chars in an extended attribute name */ +#define XATTR_SIZE_MAX 65536 /* size of an extended attribute value (64k) */ +#define XATTR_LIST_MAX 65536 /* size of extended attribute namelist (64k) */ #define RTSIG_MAX 32 --- linux-2.4.17/fs/ext3/Makefile.orig Fri Dec 21 10:41:55 2001 +++ linux-2.4.17/fs/ext3/Makefile Fri Mar 22 11:00:58 2002 @@ -8,6 +8,8 @@ # Note 2! The CFLAGS definitions are now in the main makefile... O_TARGET := ext3.o + +export-objs := super.o obj-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ ioctl.o namei.o super.o symlink.o --- linux-2.4.17/fs/ext3/super.c.orig Fri Dec 21 10:41:55 2001 +++ linux-2.4.17/fs/ext3/super.c Fri Mar 22 11:00:41 2002 @@ -1742,7 +1742,7 @@ unregister_filesystem(&ext3_fs_type); } -EXPORT_NO_SYMBOLS; +EXPORT_SYMBOL(ext3_bread); MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others"); MODULE_DESCRIPTION("Second Extended Filesystem with journaling extensions"); --- lum/include/linux/blkdev.h.orig Thu Mar 28 11:40:24 2002 +++ lum/include/linux/blkdev.h Thu Mar 28 23:38:46 2002 @@ -228,4 +228,8 @@ return retval; } +#define CONFIG_DEV_RDONLY +void dev_set_rdonly(kdev_t, int); +int dev_check_rdonly(kdev_t); +void dev_clear_rdonly(int); #endif --- lum/drivers/block/blkpg.c.orig Sun Nov 11 11:20:21 2001 +++ lum/drivers/block/blkpg.c Thu Mar 28 16:30:41 2002 @@ -294,3 +294,38 @@ } EXPORT_SYMBOL(blk_ioctl); + +#define NUM_DEV_NO_WRITE 16 +static int dev_no_write[NUM_DEV_NO_WRITE]; + +/* + * Debug code for turning block devices "read-only" (will discard writes + * silently). This is for filesystem crash/recovery testing. + */ +void dev_set_rdonly(kdev_t dev, int no_write) +{ + if (dev) { + printk(KERN_WARNING "Turning device %s read-only\n", + bdevname(dev)); + dev_no_write[no_write] = 0xdead0000 + dev; + } +} + +int dev_check_rdonly(kdev_t dev) { + int i; + + for (i = 0; i < NUM_DEV_NO_WRITE; i++) { + if ((dev_no_write[i] & 0xffff0000) == 0xdead0000 && + dev == (dev_no_write[i] & 0xffff)) + return 1; + } + return 0; +} + +void dev_clear_rdonly(int no_write) { + dev_no_write[no_write] = 0; +} + +EXPORT_SYMBOL(dev_set_rdonly); +EXPORT_SYMBOL(dev_check_rdonly); +EXPORT_SYMBOL(dev_clear_rdonly); --- lum/drivers/block/loop.c.orig Fri Dec 21 10:41:53 2001 +++ lum/drivers/block/loop.c Thu Mar 28 23:39:25 2002 @@ -471,6 +475,11 @@ spin_unlock_irq(&lo->lo_lock); if (rw == WRITE) { +#ifdef CONFIG_DEV_RDONLY + if (dev_check_rdonly(rbh->b_rdev)) + goto err; +#endif + if (lo->lo_flags & LO_FLAGS_READ_ONLY) goto err; } else if (rw == READA) { --- lum/drivers/ide/ide-disk.c.orig Fri Dec 21 10:41:54 2001 +++ lum/drivers/ide/ide-disk.c Thu Mar 28 23:38:41 2002 @@ -367,6 +367,12 @@ */ static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) { +#ifdef CONFIG_DEV_RDONLY + if (rq->cmd == WRITE && dev_check_rdonly(rq->rq_dev)) { + ide_end_request(1, HWGROUP(drive)); + return ide_stopped; + } +#endif if (IDE_CONTROL_REG) OUT_BYTE(drive->ctl,IDE_CONTROL_REG); OUT_BYTE(0x00, IDE_FEATURE_REG); --- lum/include/linux/jbd.h.orig Tue Apr 16 14:27:25 2002 +++ lum/include/linux/jbd.h Wed Apr 24 05:19:00 2002 @@ -249,6 +249,13 @@ return bh->b_private; } +#define HAVE_JOURNAL_CALLBACK_STATUS +struct journal_callback { + struct list_head jcb_list; + void (*jcb_func)(void *cb_data, int error); + /* user data goes here */ +}; + struct jbd_revoke_table_s; /* The handle_t type represents a single atomic update being performed @@ -279,6 +285,12 @@ operations */ int h_err; + /* List of application registered callbacks for this handle. + * The function(s) will be called after the transaction that + * this handle is part of has been committed to disk. + */ + struct list_head h_jcb; + /* Flags */ unsigned int h_sync: 1; /* sync-on-close */ unsigned int h_jdata: 1; /* force data journaling */ @@ -398,6 +410,10 @@ /* How many handles used this transaction? */ int t_handle_count; + + /* List of registered callback functions for this transaction. + * Called when the transaction is committed. */ + struct list_head t_jcb; }; @@ -646,6 +662,8 @@ extern int journal_try_to_free_buffers(journal_t *, struct page *, int); extern int journal_stop(handle_t *); extern int journal_flush (journal_t *); +extern void journal_callback_set(handle_t *handle, void (*fn)(void *, int), + void *cb_data); extern void journal_lock_updates (journal_t *); extern void journal_unlock_updates (journal_t *); --- lum/fs/jbd/commit.c.orig Fri Apr 12 10:27:52 2002 +++ lum/fs/jbd/commit.c Thu May 23 16:53:16 2002 @@ -475,7 +475,7 @@ transaction's t_log_list queue, and metadata buffers are on the t_iobuf_list queue. - Wait for the transactions in reverse order. That way we are + Wait for the buffers in reverse order. That way we are less likely to be woken up until all IOs have completed, and so we incur less scheduling load. */ @@ -566,8 +566,10 @@ jbd_debug(3, "JBD: commit phase 6\n"); - if (is_journal_aborted(journal)) + if (is_journal_aborted(journal)) { + unlock_journal(journal); goto skip_commit; + } /* Done it all: now write the commit record. We should have * cleaned up our previous buffers by now, so if we are in abort @@ -577,9 +579,10 @@ descriptor = journal_get_descriptor_buffer(journal); if (!descriptor) { __journal_abort_hard(journal); + unlock_journal(journal); goto skip_commit; } - + /* AKPM: buglet - add `i' to tmp! */ for (i = 0; i < jh2bh(descriptor)->b_size; i += 512) { journal_header_t *tmp = @@ -600,7 +603,6 @@ put_bh(bh); /* One for getblk() */ journal_unlock_journal_head(descriptor); } - lock_journal(journal); /* End of a transaction! Finally, we can do checkpoint processing: any buffers committed as a result of this @@ -609,6 +611,25 @@ skip_commit: + /* Call any callbacks that had been registered for handles in this + * transaction. It is up to the callback to free any allocated + * memory. + */ + if (!list_empty(&commit_transaction->t_jcb)) { + struct list_head *p, *n; + int error = is_journal_aborted(journal); + + list_for_each_safe(p, n, &commit_transaction->t_jcb) { + struct journal_callback *jcb; + + jcb = list_entry(p, struct journal_callback, jcb_list); + list_del(p); + jcb->jcb_func(jcb, error); + } + } + + lock_journal(journal); + jbd_debug(3, "JBD: commit phase 7\n"); J_ASSERT(commit_transaction->t_sync_datalist == NULL); --- lum/fs/jbd/journal.c.orig Fri Apr 12 10:27:52 2002 +++ lum/fs/jbd/journal.c Wed Apr 24 05:18:49 2002 @@ -58,6 +58,7 @@ #endif EXPORT_SYMBOL(journal_flush); EXPORT_SYMBOL(journal_revoke); +EXPORT_SYMBOL(journal_callback_set); EXPORT_SYMBOL(journal_init_dev); EXPORT_SYMBOL(journal_init_inode); --- lum/fs/jbd/transaction.c.orig Fri Apr 12 10:27:52 2002 +++ lum/fs/jbd/transaction.c Wed Apr 24 05:31:14 2002 @@ -57,6 +57,7 @@ transaction->t_state = T_RUNNING; transaction->t_tid = journal->j_transaction_sequence++; transaction->t_expires = jiffies + journal->j_commit_interval; + INIT_LIST_HEAD(&transaction->t_jcb); /* Set up the commit timer for the new transaction. */ J_ASSERT (!journal->j_commit_timer_active); @@ -201,6 +202,20 @@ return 0; } +/* Allocate a new handle. This should probably be in a slab... */ +static handle_t *get_handle(int nblocks) +{ + handle_t *handle = jbd_kmalloc(sizeof (handle_t), GFP_NOFS); + if (!handle) + return NULL; + memset(handle, 0, sizeof (handle_t)); + handle->h_buffer_credits = nblocks; + handle->h_ref = 1; + INIT_LIST_HEAD(&handle->h_jcb); + + return handle; +} + /* * Obtain a new handle. * @@ -227,14 +242,11 @@ handle->h_ref++; return handle; } - - handle = jbd_kmalloc(sizeof (handle_t), GFP_NOFS); + + handle = get_handle(nblocks); if (!handle) return ERR_PTR(-ENOMEM); - memset (handle, 0, sizeof (handle_t)); - handle->h_buffer_credits = nblocks; - handle->h_ref = 1; current->journal_info = handle; err = start_this_handle(journal, handle); @@ -333,14 +345,11 @@ if (is_journal_aborted(journal)) return ERR_PTR(-EIO); - - handle = jbd_kmalloc(sizeof (handle_t), GFP_NOFS); + + handle = get_handle(nblocks); if (!handle) return ERR_PTR(-ENOMEM); - memset (handle, 0, sizeof (handle_t)); - handle->h_buffer_credits = nblocks; - handle->h_ref = 1; current->journal_info = handle; err = try_start_this_handle(journal, handle); @@ -1328,6 +1337,29 @@ #endif /* + * Register a callback function for this handle. The function will be + * called when the transaction that this handle is part of has been + * committed to disk with the original callback data struct and the + * error status of the journal as parameters. There is no guarantee of + * ordering between handles within a single transaction, nor between + * callbacks registered on the same handle. + * + * The caller is responsible for allocating the journal_callback struct. + * This is to allow the caller to add as much extra data to the callback + * as needed, but reduce the overhead of multiple allocations. The caller + * allocated struct must start with a struct journal_callback at offset 0, + * and has the caller-specific data afterwards. + */ +void journal_callback_set(handle_t *handle, void (*func)(void *, int), + void *cb_data) +{ + struct journal_callback *jcb = cb_data; + + list_add(&jcb->jcb_list, &handle->h_jcb); + jcb->jcb_func = func; +} + +/* * All done for a particular handle. * * There is not much action needed here. We just return any remaining @@ -1393,7 +1423,10 @@ wake_up(&journal->j_wait_transaction_locked); } - /* + /* Move callbacks from the handle to the transaction. */ + list_splice(&handle->h_jcb, &transaction->t_jcb); + + /* * If the handle is marked SYNC, we need to set another commit * going! We also want to force a commit if the current * transaction is occupying too much of the log, or if the