]> git.neil.brown.name Git - history.git/commitdiff
Import 2.3.7pre9 2.3.7pre9
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:25:37 +0000 (15:25 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:25:37 +0000 (15:25 -0500)
fs/ext2/fsync.c
fs/proc/array.c
fs/proc/root.c
include/linux/fs.h
include/linux/ioport.h
include/linux/proc_fs.h
kernel/ksyms.c
kernel/resource.c
mm/filemap.c

index b6dd9bce6cda35b3aa28e1b32adab63ad7fafa3d..8ae361e7334aadbb3a813316c79b396184a3bdea 100644 (file)
@@ -17,6 +17,9 @@
  *  Removed unnecessary code duplication for little endian machines
  *  and excessive __inline__s. 
  *        Andi Kleen, 1997
+ *
+ * Major simplications and cleanup - we only need to do the metadata, because
+ * we can depend on generic_block_fdatasync() to sync the data blocks.
  */
 
 #include <asm/uaccess.h>
@@ -32,8 +35,8 @@
 #include <linux/locks.h>
 
 
-#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb))
-#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb))
+#define blocksize      (EXT2_BLOCK_SIZE(inode->i_sb))
+#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb))
 
 static int sync_indirect(struct inode * inode, u32 * block, int wait)
 {
@@ -41,23 +44,23 @@ static int sync_indirect(struct inode * inode, u32 * block, int wait)
        
        if (!*block)
                return 0;
-       bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize);
+       bh = get_hash_table(inode->i_dev, le32_to_cpu(*block), blocksize);
        if (!bh)
                return 0;
        if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
-               brelse (bh);
+               brelse(bh);
                return -1;
        }
        if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
-               brelse (bh);
+               brelse(bh);
                return 0;
        }
-       ll_rw_block (WRITE, 1, &bh);
+       ll_rw_block(WRITE, 1, &bh);
        bh->b_count--;
        return 0;
 }
 
-static int sync_iblock (struct inode * inode, u32 * iblock, 
+static int sync_iblock(struct inode * inode, u32 * iblock, 
                        struct buffer_head ** bh, int wait) 
 {
        int rc, tmp;
@@ -69,51 +72,47 @@ static int sync_iblock (struct inode * inode, u32 * iblock,
        rc = sync_indirect(inode, iblock, wait);
        if (rc)
                return rc;
-       *bh = bread (inode->i_dev, tmp, blocksize);
+       *bh = bread(inode->i_dev, tmp, blocksize);
        if (!*bh)
                return -1;
        return 0;
 }
 
-static int sync_dindirect (struct inode * inode, u32 * diblock, int wait)
+static int sync_dindirect(struct inode * inode, u32 * diblock, int wait)
 {
        int i;
        struct buffer_head * dind_bh;
        int rc, err = 0;
 
-       rc = sync_iblock (inode, diblock, &dind_bh, wait);
+       rc = sync_iblock(inode, diblock, &dind_bh, wait);
        if (rc || !dind_bh)
                return rc;
        
        for (i = 0; i < addr_per_block; i++) {
-               rc = sync_indirect(inode,
-                                  ((u32 *) dind_bh->b_data) + i,
-                                  wait);
+               rc = sync_indirect(inode, ((u32 *) dind_bh->b_data) + i, wait);
                if (rc)
                        err = rc;
        }
-       brelse (dind_bh);
+       brelse(dind_bh);
        return err;
 }
 
-static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait)
+static int sync_tindirect(struct inode * inode, u32 * tiblock, int wait)
 {
        int i;
        struct buffer_head * tind_bh;
        int rc, err = 0;
 
-       rc = sync_iblock (inode, tiblock, &tind_bh, wait);
+       rc = sync_iblock(inode, tiblock, &tind_bh, wait);
        if (rc || !tind_bh)
                return rc;
        
        for (i = 0; i < addr_per_block; i++) {
-               rc = sync_dindirect(inode,
-                                   ((u32 *) tind_bh->b_data) + i,
-                                   wait);
+               rc = sync_dindirect(inode, ((u32 *) tind_bh->b_data) + i, wait);
                if (rc)
                        err = rc;
        }
-       brelse (tind_bh);
+       brelse(tind_bh);
        return err;
 }
 
@@ -137,15 +136,15 @@ int ext2_sync_file(struct file * file, struct dentry *dentry)
 
        for (wait=0; wait<=1; wait++)
        {
-               err |= sync_indirect (inode,
-                                     inode->u.ext2_i.i_data+EXT2_IND_BLOCK,
+               err |= sync_indirect(inode,
+                                    inode->u.ext2_i.i_data+EXT2_IND_BLOCK,
+                                    wait);
+               err |= sync_dindirect(inode,
+                                     inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, 
+                                     wait);
+               err |= sync_tindirect(inode, 
+                                     inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, 
                                      wait);
-               err |= sync_dindirect (inode,
-                                      inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, 
-                                      wait);
-               err |= sync_tindirect (inode, 
-                                      inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, 
-                                      wait);
        }
 skip:
        err |= ext2_sync_inode (inode);
index 4e766ca2b5eb7d587d7aeee1bfb3a9614e66e64d..d2ec8eae670b9306a04415a8a00d91c2c8b0ac7d 100644 (file)
@@ -1326,6 +1326,9 @@ static long get_root_array(char * page, int type, char **start,
 
                case PROC_IOPORTS:
                        return get_ioport_list(page);
+
+               case PROC_MEMORY:
+                       return get_mem_list(page);
 #ifdef CONFIG_BLK_DEV_MD
                case PROC_MD:
                        return get_md_status(page);
index ee4acbd261a938b8f857fb0a124c20f31621fc7e..79622b022e2c1abe67d63a213037957d45fb4672 100644 (file)
@@ -621,6 +621,11 @@ static struct proc_dir_entry proc_root_ioports = {
        S_IFREG | S_IRUGO, 1, 0, 0,
        0, &proc_array_inode_operations
 };
+static struct proc_dir_entry proc_root_memory = {
+       PROC_MEMORY, 6, "memory",
+       S_IFREG | S_IRUGO, 1, 0, 0,
+       0, &proc_array_inode_operations
+};
 static struct proc_dir_entry proc_root_cmdline = {
        PROC_CMDLINE, 7, "cmdline",
        S_IFREG | S_IRUGO, 1, 0, 0,
@@ -709,6 +714,7 @@ __initfunc(void proc_root_init(void))
        proc_register(&proc_root, &proc_root_fs);
        proc_register(&proc_root, &proc_root_dma);
        proc_register(&proc_root, &proc_root_ioports);
+       proc_register(&proc_root, &proc_root_memory);
        proc_register(&proc_root, &proc_root_cmdline);
 #ifdef CONFIG_RTC
        proc_register(&proc_root, &proc_root_rtc);
index d5029c263bc1558030f311822f30db88d0faa028..a613816aad1b4d7e23b17ffdfb666bb356a19e76 100644 (file)
@@ -234,30 +234,13 @@ struct buffer_head {
 typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate);
 void init_buffer(struct buffer_head *, kdev_t, int, bh_end_io_t *, void *);
 
-static inline int buffer_uptodate(struct buffer_head * bh)
-{
-       return test_bit(BH_Uptodate, &bh->b_state);
-}      
-
-static inline int buffer_dirty(struct buffer_head * bh)
-{
-       return test_bit(BH_Dirty, &bh->b_state);
-}
-
-static inline int buffer_locked(struct buffer_head * bh)
-{
-       return test_bit(BH_Lock, &bh->b_state);
-}
-
-static inline int buffer_req(struct buffer_head * bh)
-{
-       return test_bit(BH_Req, &bh->b_state);
-}
+#define __buffer_state(bh, state)      (((bh)->b_state & (1UL << BH_##state)) != 0)
 
-static inline int buffer_protected(struct buffer_head * bh)
-{
-       return test_bit(BH_Protected, &bh->b_state);
-}
+#define buffer_uptodate(bh)    __buffer_state(bh,Uptodate)
+#define buffer_dirty(bh)       __buffer_state(bh,Dirty)
+#define buffer_locked(bh)      __buffer_state(bh,Lock)
+#define buffer_req(bh)         __buffer_state(bh,Req)
+#define buffer_protected(bh)   __buffer_state(bh,Protected)
 
 #define buffer_page(bh)                (mem_map + MAP_NR((bh)->b_data))
 #define touch_buffer(bh)       set_bit(PG_referenced, &buffer_page(bh)->flags)
index b5eef44dd3dc42a8119b2df43cae987036235581..2f729f96b2562d9f8a35f1cc83f586819d57f300 100644 (file)
@@ -1,14 +1,38 @@
 /*
- * portio.h    Definitions of routines for detecting, reserving and
+ * ioport.h    Definitions of routines for detecting, reserving and
  *             allocating system resources.
  *
- * Version:    0.01    8/30/93
- *
- * Author:     Donald Becker (becker@super.org)
+ * Authors:    Donald Becker (becker@cesdis.gsfc.nasa.gov)
+ *             David Hinds (dhinds@zen.stanford.edu)
  */
 
-#ifndef _LINUX_PORTIO_H
-#define _LINUX_PORTIO_H
+#ifndef _LINUX_IOPORT_H
+#define _LINUX_IOPORT_H
+
+#define RES_IO         0
+#define RES_MEM                1
+
+extern void reserve_setup(char *str, int *ints);
+
+extern struct resource_entry *iolist, *memlist;
+
+extern int get_resource_list(int class, char *buf);
+extern int check_resource(int class,
+                         unsigned long from, unsigned long extent);
+extern void request_resource(int class,
+                            unsigned long from, unsigned long extent,
+                            const char *name);
+extern void release_resource(int class,
+                            unsigned long from, unsigned long extent);
+extern unsigned long occupy_resource(int class,
+                                    unsigned long base, unsigned long end,
+                                    unsigned long num, unsigned long align,
+                                    const char *name);
+extern void vacate_resource(int class,
+                           unsigned long from, unsigned long extent);
+
+#define get_ioport_list(buf)   get_resource_list(RES_IO, buf)
+#define get_mem_list(buf)      get_resource_list(RES_MEM, buf)
 
 #define HAVE_PORTRESERVE
 /*
  * Once you have found you hardware, register it with request_region().
  * If you unload the driver, use release_region to free ports.
  */
-extern void reserve_setup(char *str, int *ints);
-extern int check_region(unsigned long from, unsigned long extent);
-extern void request_region(unsigned long from, unsigned long extent,const char *name);
-extern void release_region(unsigned long from, unsigned long extent);
-extern int get_ioport_list(char *);
+#define check_region(f,e)              check_resource(RES_IO,f,e)
+#define request_region(f,e,n)          request_resource(RES_IO,f,e,n)
+#define release_region(f,e)            release_resource(RES_IO,f,e)
+#define occupy_region(b,e,n,a,s)       occupy_resource(RES_IO,b,e,n,a,s)
+#define vacate_region(f,e)             vacate_resource(RES_IO,f,e)
 
-#ifdef __sparc__
-extern unsigned long occupy_region(unsigned long base, unsigned long end,
-                                  unsigned long num, unsigned int align,
-                                  const char *name);
-#endif
+#define HAVE_MEMRESERVE
+#define check_mem_region(f,e)          check_resource(RES_MEM,f,e)
+#define request_mem_region(f,e,n)      request_resource(RES_MEM,f,e,n)
+#define release_mem_region(f,e)                release_resource(RES_MEM,f,e)
+#define occupy_mem_region(b,e,n,a,s)   occupy_resource(RES_MEM,b,e,n,a,s)
+#define vacate_mem_region(f,e)         vacate_resource(RES_MEM,f,e)
 
 #define HAVE_AUTOIRQ
 extern void autoirq_setup(int waittime);
 extern int autoirq_report(int waittime);
 
-#endif /* _LINUX_PORTIO_H */
+#endif /* _LINUX_IOPORT_H */
index 6297457d299321f1a2e784fd0b26851c347a94e6..5c54d05bc585fd88b00f4811d1559179a690d0bc 100644 (file)
@@ -37,6 +37,7 @@ enum root_directory_inos {
        PROC_KSYMS,
        PROC_DMA,       
        PROC_IOPORTS,
+       PROC_MEMORY,
        PROC_PROFILE, /* whether enabled or not */
        PROC_CMDLINE,
        PROC_SYS,
index c9742a18a98cb9a6df12c73144e45d99b85c4301..03c5167100eece9ec10ee0f880b97c9cfa7bf8e1 100644 (file)
@@ -300,10 +300,12 @@ EXPORT_SYMBOL(disable_hlt);
 EXPORT_SYMBOL(enable_hlt);
 #endif
 
-/* IO port handling */
-EXPORT_SYMBOL(check_region);
-EXPORT_SYMBOL(request_region);
-EXPORT_SYMBOL(release_region);
+/* resource handling */
+EXPORT_SYMBOL(check_resource);
+EXPORT_SYMBOL(request_resource);
+EXPORT_SYMBOL(release_resource);
+EXPORT_SYMBOL(occupy_resource);
+EXPORT_SYMBOL(vacate_resource);
 
 /* process management */
 EXPORT_SYMBOL(__wake_up);
index fa607edf8a71061396ef3838a2c8ef0b0927e045..4c672c6b0113c69817072fee66d8dc0f2a438bee 100644 (file)
@@ -1,10 +1,17 @@
 /*
  *     linux/kernel/resource.c
  *
- * Copyright (C) 1995  Linus Torvalds
- *                     David Hinds
+ * Copyright (C) 1995, 1999    Linus Torvalds
+ *                             David Hinds
  *
- * Kernel io-region resource management
+ * Kernel resource management
+ *
+ * We now distinguish between claiming space for devices (using the
+ * 'occupy' and 'vacate' calls), and associating a resource with a
+ * device driver (with the 'request', 'release', and 'check' calls).
+ * A resource can be claimed even if there is no associated driver
+ * (by occupying with name=NULL).  Vacating a resource makes it
+ * available for other dynamically configured devices.
  */
 
 #include <linux/sched.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
 
-#define IOTABLE_SIZE 128
+#define RSRC_TABLE_SIZE 128
 
-typedef struct resource_entry_t {
+struct resource_entry {
        u_long from, num;
        const char *name;
-       struct resource_entry_t *next;
-} resource_entry_t;
+       struct resource_entry *next;
+};
 
-static resource_entry_t iolist = { 0, 0, "", NULL };
+struct resource_entry res_list[] = {
+    { 0, 0, NULL, NULL }, /* IO */
+    { 0, 0, NULL, NULL }  /* mem */
+};
 
-static resource_entry_t iotable[IOTABLE_SIZE];
+static struct resource_entry rsrc_table[RSRC_TABLE_SIZE];
 
 /*
- * This generates the report for /proc/ioports
+ * This generates reports for /proc/ioports and /proc/memory
  */
-int get_ioport_list(char *buf)
+int get_resource_list(int class, char *buf)
 {
-       resource_entry_t *p;
+       struct resource_entry *root = &res_list[class];
+       struct resource_entry *p;
        int len = 0;
-
-       for (p = iolist.next; (p) && (len < 4000); p = p->next)
-               len += sprintf(buf+len, "%04lx-%04lx : %s\n",
-                          p->from, p->from+p->num-1, p->name);
+       char *fmt = (class == RES_IO) ?
+               "%04lx-%04lx : %s\n" : "%08lx-%08lx : %s\n";
+       
+       for (p = root->next; (p) && (len < 4000); p = p->next)
+               len += sprintf(buf+len, fmt, p->from, p->from+p->num-1,
+                              (p->name ? p->name : "occupied"));
        if (p)
                len += sprintf(buf+len, "4K limit reached!\n");
        return len;
 }
 
 /*
- * The workhorse function: find where to put a new entry
+ * Basics: find a matching resource entry, or find an insertion point
  */
-static resource_entry_t *find_gap(resource_entry_t *root,
-                                 u_long from, u_long num)
+static struct resource_entry *
+find_match(struct resource_entry *root, u_long from, u_long num)
 {
-       unsigned long flags;
-       resource_entry_t *p;
-       
+       struct resource_entry *p;
+       for (p = root; p; p = p->next)
+               if ((p->from == from) && (p->num == num))
+                       return p;
+       return NULL;
+}
+
+static struct resource_entry *
+find_gap(struct resource_entry *root, u_long from, u_long num)
+{
+       struct resource_entry *p;
        if (from > from+num-1)
                return NULL;
-       save_flags(flags);
-       cli();
        for (p = root; ; p = p->next) {
                if ((p != root) && (p->from+p->num-1 >= from)) {
                        p = NULL;
@@ -61,123 +80,147 @@ static resource_entry_t *find_gap(resource_entry_t *root,
                if ((p->next == NULL) || (p->next->from > from+num-1))
                        break;
        }
-       restore_flags(flags);
        return p;
 }
 
 /*
- * Call this from the device driver to register the ioport region.
+ * Call this from a driver to assert ownership of a resource
  */
-void request_region(unsigned long from, unsigned long num, const char *name)
+void request_resource(int class, unsigned long from,
+                     unsigned long num, const char *name)
 {
-       resource_entry_t *p;
+       struct resource_entry *root = &res_list[class];
+       struct resource_entry *p;
+       long flags;
        int i;
 
-       for (i = 0; i < IOTABLE_SIZE; i++)
-               if (iotable[i].num == 0)
+       p = find_match(root, from, num);
+       if (p) {
+               p->name = name;
+               return;
+       }
+
+       save_flags(flags);
+       cli();
+       for (i = 0; i < RSRC_TABLE_SIZE; i++)
+               if (rsrc_table[i].num == 0)
                        break;
-       if (i == IOTABLE_SIZE)
-               printk("warning: ioport table is full\n");
+       if (i == RSRC_TABLE_SIZE)
+               printk("warning: resource table is full\n");
        else {
-               p = find_gap(&iolist, from, num);
-               if (p == NULL)
+               p = find_gap(root, from, num);
+               if (p == NULL) {
+                       restore_flags(flags);
                        return;
-               iotable[i].name = name;
-               iotable[i].from = from;
-               iotable[i].num = num;
-               iotable[i].next = p->next;
-               p->next = &iotable[i];
-               return;
+               }
+               rsrc_table[i].name = name;
+               rsrc_table[i].from = from;
+               rsrc_table[i].num = num;
+               rsrc_table[i].next = p->next;
+               p->next = &rsrc_table[i];
        }
+       restore_flags(flags);
 }
 
 /* 
- * Call this when the device driver is unloaded
+ * Call these when a driver is unloaded but the device remains
  */
-void release_region(unsigned long from, unsigned long num)
+void release_resource(int class, unsigned long from, unsigned long num)
 {
-       resource_entry_t *p, *q;
-
-       for (p = &iolist; ; p = q) {
-               q = p->next;
-               if (q == NULL)
-                       break;
-               if ((q->from == from) && (q->num == num)) {
-                       q->num = 0;
-                       p->next = q->next;
-                       return;
-               }
-       }
+       struct resource_entry *root = &res_list[class];
+       struct resource_entry *p;
+       p = find_match(root, from, num);
+       if (p) p->name = NULL;
 }
 
 /*
- * Call this to check the ioport region before probing
+ * Call these to check a region for conflicts before probing
  */
-int check_region(unsigned long from, unsigned long num)
+int check_resource(int class, unsigned long from, unsigned long num)
 {
-       return (find_gap(&iolist, from, num) == NULL) ? -EBUSY : 0;
+       struct resource_entry *root = &res_list[class];
+       struct resource_entry *p;
+       p = find_match(root, from, num);
+       if (p != NULL)
+               return (p->name != NULL) ? -EBUSY : 0;
+       return (find_gap(root, from, num) == NULL) ? -EBUSY : 0;
 }
 
-#ifdef __sparc__   /* Why to carry unused code on other architectures? */
 /*
- * This is for architectures with MMU-managed ports (sparc).
+ * Call this to claim a resource for a piece of hardware
  */
-unsigned long occupy_region(unsigned long base, unsigned long end,
-                           unsigned long num, unsigned int align, const char *name)
+unsigned long occupy_resource(int class, unsigned long base,
+                             unsigned long end, unsigned long num,
+                             unsigned long align, const char *name)
 {
+       struct resource_entry *root = &res_list[class];
        unsigned long from = 0, till;
        unsigned long flags;
        int i;
-       resource_entry_t *p;            /* Scanning ptr */
-       resource_entry_t *p1;           /* === p->next */
-       resource_entry_t *s;            /* Found slot */
+       struct resource_entry *p, *q;
 
-       if (base > end-1)
-               return 0;
-       if (num > end - base)
+       if ((base > end-1) || (num > end - base))
                return 0;
 
-       for (i = 0; i < IOTABLE_SIZE; i++)
-               if (iotable[i].num == 0)
+       for (i = 0; i < RSRC_TABLE_SIZE; i++)
+               if (rsrc_table[i].num == 0)
                        break;
-       if (i == IOTABLE_SIZE) {
-               /* Driver prints a warning typically. */
+       if (i == RSRC_TABLE_SIZE)
                return 0;
-       }
 
        save_flags(flags);
        cli();
        /* printk("occupy: search in %08lx[%08lx] ", base, end - base); */
-       s = NULL;
-       for (p = &iolist; p != NULL; p = p1) {
-               p1 = p->next;
+       for (p = root; p != NULL; p = q) {
+               q = p->next;
                /* Find window in list */
-               from = (p->from+p->num + align-1) & ~((unsigned long)align-1);
-               till = (p1 == NULL)? (unsigned long) (0 - (unsigned long)align): p1->from;
+               from = (p->from+p->num + align-1) & ~(align-1);
+               till = (q == NULL) ? (0 - align) : q->from;
                /* printk(" %08lx:%08lx", from, till); */
                /* Clip window with base and end */
                if (from < base) from = base;
                if (till > end) till = end;
                /* See if result is large enougth */
-               if (from < till && from + num < till) {
-                       s = p;
+               if ((from < till) && (from + num < till))
                        break;
-               }
        }
        /* printk("\r\n"); */
        restore_flags(flags);
 
-       if (s == NULL)
+       if (p == NULL)
                return 0;
 
-       iotable[i].name = name;
-       iotable[i].from = from;
-       iotable[i].num = num;
-       iotable[i].next = s->next;
-       s->next = &iotable[i];
+       rsrc_table[i].name = name;
+       rsrc_table[i].from = from;
+       rsrc_table[i].num = num;
+       rsrc_table[i].next = p->next;
+       p->next = &rsrc_table[i];
        return from;
 }
-#endif
+
+/*
+ * Call this when a resource becomes available for other hardware
+ */
+void vacate_resource(int class, unsigned long from, unsigned long num)
+{
+       struct resource_entry *root = &res_list[class];
+       struct resource_entry *p, *q;
+       long flags;
+
+       save_flags(flags);
+       cli();
+       for (p = root; ; p = q) {
+               q = p->next;
+               if (q == NULL)
+                       break;
+               if ((q->from == from) && (q->num == num)) {
+                       q->num = 0;
+                       p->next = q->next;
+                       break;
+               }
+       }
+       restore_flags(flags);
+}
 
 /* Called from init/main.c to reserve IO ports. */
 void __init reserve_setup(char *str, int *ints)
index d8fcac02c03fba8f0fb970083dbf0570c0efd6e0..c0a17a00f595c23923d0943c96e537ddeb1b4b10 100644 (file)
@@ -359,7 +359,7 @@ static int writeout_one_page(struct page *page)
 
        bh = head;
        do {
-               if (buffer_locked(bh) || !buffer_dirty(bh))
+               if (buffer_locked(bh) || !buffer_dirty(bh) || !buffer_uptodate(bh))
                        continue;
 
                bh->b_flushtime = 0;
@@ -376,7 +376,7 @@ static int waitfor_one_page(struct page *page)
        bh = head;
        do {
                wait_on_buffer(bh);
-               if (!buffer_uptodate(bh))
+               if (buffer_req(bh) && !buffer_uptodate(bh))
                        error = -EIO;
        } while ((bh = bh->b_this_page) != head);
        return error;