block: fix buffer overflow when printing partition UUIDs
[linux-flexiantxendom0-3.2.10.git] / include / linux / genhd.h
index 9cb8380..017a7fb 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/types.h>
 #include <linux/kdev_t.h>
 #include <linux/rcupdate.h>
+#include <linux/slab.h>
 
 #ifdef CONFIG_BLOCK
 
@@ -25,9 +26,6 @@ extern struct device_type part_type;
 extern struct kobject *block_depr;
 extern struct class block_class;
 
-extern const struct seq_operations partitions_op;
-extern const struct seq_operations diskstats_op;
-
 enum {
 /* These three have identical behaviour; use the second one if DOS FDISK gets
    confused about extended/logical partitions starting past cylinder 1023. */
@@ -58,6 +56,9 @@ enum {
        UNIXWARE_PARTITION = 0x63,      /* Same as GNU_HURD and SCO Unix */
 };
 
+#define DISK_MAX_PARTS                 256
+#define DISK_NAME_LEN                  32
+
 #include <linux/major.h>
 #include <linux/device.h>
 #include <linux/smp.h>
@@ -86,79 +87,110 @@ struct disk_stats {
        unsigned long io_ticks;
        unsigned long time_in_queue;
 };
-       
+
+#define PARTITION_META_INFO_VOLNAMELTH 64
+#define PARTITION_META_INFO_UUIDLTH    16
+
+struct partition_meta_info {
+       u8 uuid[PARTITION_META_INFO_UUIDLTH];   /* always big endian */
+       u8 volname[PARTITION_META_INFO_VOLNAMELTH];
+};
+
 struct hd_struct {
        sector_t start_sect;
        sector_t nr_sects;
+       sector_t alignment_offset;
+       unsigned int discard_alignment;
        struct device __dev;
        struct kobject *holder_dir;
        int policy, partno;
+       struct partition_meta_info *info;
 #ifdef CONFIG_FAIL_MAKE_REQUEST
        int make_it_fail;
 #endif
        unsigned long stamp;
-       int in_flight;
+       atomic_t in_flight[2];
 #ifdef CONFIG_SMP
-       struct disk_stats *dkstats;
+       struct disk_stats __percpu *dkstats;
 #else
        struct disk_stats dkstats;
 #endif
+       atomic_t ref;
        struct rcu_head rcu_head;
 };
 
 #define GENHD_FL_REMOVABLE                     1
-#define GENHD_FL_DRIVERFS                      2
+/* 2 is unused */
 #define GENHD_FL_MEDIA_CHANGE_NOTIFY           4
 #define GENHD_FL_CD                            8
 #define GENHD_FL_UP                            16
 #define GENHD_FL_SUPPRESS_PARTITION_INFO       32
-#define GENHD_FL_FAIL                          64
+#define GENHD_FL_EXT_DEVT                      64 /* allow extended devt */
+#define GENHD_FL_NATIVE_CAPACITY               128
+#define GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE    256
+#define GENHD_FL_NO_PART_SCAN                  512
+
+enum {
+       DISK_EVENT_MEDIA_CHANGE                 = 1 << 0, /* media changed */
+       DISK_EVENT_EJECT_REQUEST                = 1 << 1, /* eject requested */
+};
+
+#define BLK_SCSI_MAX_CMDS      (256)
+#define BLK_SCSI_CMD_PER_LONG  (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8))
+
+struct blk_scsi_cmd_filter {
+       unsigned long read_ok[BLK_SCSI_CMD_PER_LONG];
+       unsigned long write_ok[BLK_SCSI_CMD_PER_LONG];
+       struct kobject kobj;
+};
+
+struct disk_part_tbl {
+       struct rcu_head rcu_head;
+       int len;
+       struct hd_struct __rcu *last_lookup;
+       struct hd_struct __rcu *part[];
+};
+
+struct disk_events;
 
 struct gendisk {
-       /* major, first_minor, minors and ext_minors are input
-        * parameters only, don't use directly.  Use disk_devt() and
-        * disk_max_parts().
+       /* major, first_minor and minors are input parameters only,
+        * don't use directly.  Use disk_devt() and disk_max_parts().
         */
        int major;                      /* major number of driver */
        int first_minor;
        int minors;                     /* maximum number of minors, =1 for
                                          * disks that can't be partitioned. */
-       int ext_minors;                 /* number of extended dynamic minors */
 
-       char disk_name[32];             /* name of major driver */
+       char disk_name[DISK_NAME_LEN];  /* name of major driver */
+       char *(*devnode)(struct gendisk *gd, umode_t *mode);
+
+       unsigned int events;            /* supported events */
+       unsigned int async_events;      /* async events, subset of all */
 
        /* Array of pointers to partitions indexed by partno.
         * Protected with matching bdev lock but stat and other
         * non-critical accesses use RCU.  Always access through
         * helpers.
         */
-       struct hd_struct **__part;
+       struct disk_part_tbl __rcu *part_tbl;
        struct hd_struct part0;
 
-       struct block_device_operations *fops;
+       const struct block_device_operations *fops;
        struct request_queue *queue;
        void *private_data;
 
        int flags;
        struct device *driverfs_dev;  // FIXME: remove
-       struct kobject *holder_dir;
        struct kobject *slave_dir;
 
        struct timer_rand_state *random;
-       int policy;
-
        atomic_t sync_io;               /* RAID */
-       unsigned long stamp;
-       int in_flight;
-#ifdef CONFIG_SMP
-       struct disk_stats *dkstats;
-#else
-       struct disk_stats dkstats;
-#endif
-       struct work_struct async_notify;
+       struct disk_events *ev;
 #ifdef  CONFIG_BLK_DEV_INTEGRITY
        struct blk_integrity *integrity;
 #endif
+       int node_id;
 };
 
 static inline struct gendisk *part_to_disk(struct hd_struct *part)
@@ -172,14 +204,35 @@ static inline struct gendisk *part_to_disk(struct hd_struct *part)
        return NULL;
 }
 
+static inline void part_pack_uuid(const u8 *uuid_str, u8 *to)
+{
+       int i;
+       for (i = 0; i < 16; ++i) {
+               *to++ = (hex_to_bin(*uuid_str) << 4) |
+                       (hex_to_bin(*(uuid_str + 1)));
+               uuid_str += 2;
+               switch (i) {
+               case 3:
+               case 5:
+               case 7:
+               case 9:
+                       uuid_str++;
+                       continue;
+               }
+       }
+}
+
 static inline int disk_max_parts(struct gendisk *disk)
 {
-       return disk->minors + disk->ext_minors;
+       if (disk->flags & GENHD_FL_EXT_DEVT)
+               return DISK_MAX_PARTS;
+       return disk->minors;
 }
 
-static inline bool disk_partitionable(struct gendisk *disk)
+static inline bool disk_part_scan_enabled(struct gendisk *disk)
 {
-       return disk_max_parts(disk) > 1;
+       return disk_max_parts(disk) > 1 &&
+               !(disk->flags & GENHD_FL_NO_PART_SCAN);
 }
 
 static inline dev_t disk_devt(struct gendisk *disk)
@@ -206,6 +259,7 @@ static inline void disk_put_part(struct hd_struct *part)
 #define DISK_PITER_REVERSE     (1 << 0) /* iterate in the reverse direction */
 #define DISK_PITER_INCL_EMPTY  (1 << 1) /* include 0-sized parts */
 #define DISK_PITER_INCL_PART0  (1 << 2) /* include partition 0 */
+#define DISK_PITER_INCL_EMPTY_PART0 (1 << 3) /* include empty partition 0 */
 
 struct disk_part_iter {
        struct gendisk          *disk;
@@ -235,46 +289,18 @@ extern struct hd_struct *disk_map_sector_rcu(struct gendisk *disk,
  * internal use only.
  */
 #ifdef CONFIG_SMP
-#define disk_stat_lock()       ({ rcu_read_lock(); get_cpu(); })
-#define disk_stat_unlock()     do { put_cpu(); rcu_read_unlock(); } while (0)
-
-#define disk_stat_add(cpu, gendiskp, field, addnd)                     \
-       (per_cpu_ptr(gendiskp->dkstats, cpu)->field += addnd)
-
-#define disk_stat_read(gendiskp, field)                                        \
-({                                                                     \
-       typeof(gendiskp->dkstats->field) res = 0;                       \
-       int i;                                                          \
-       for_each_possible_cpu(i)                                        \
-               res += per_cpu_ptr(gendiskp->dkstats, i)->field;        \
-       res;                                                            \
-})
-
-static inline void disk_stat_set_all(struct gendisk *gendiskp, int value)
-{
-       int i;
+#define part_stat_lock()       ({ rcu_read_lock(); get_cpu(); })
+#define part_stat_unlock()     do { put_cpu(); rcu_read_unlock(); } while (0)
 
-       for_each_possible_cpu(i)
-               memset(per_cpu_ptr(gendiskp->dkstats, i), value,
-                               sizeof(struct disk_stats));
-}              
-
-#define part_stat_add(cpu, part, field, addnd)                         \
-       (per_cpu_ptr(part->dkstats, cpu)->field += addnd)
-
-#define all_stat_add(cpu, gendiskp, part, field, addnd, sector)                \
-({                                                                     \
-       if (part)                                                       \
-               part_stat_add(cpu, part, field, addnd);                 \
-       disk_stat_add(cpu, gendiskp, field, addnd);                     \
-})
+#define __part_stat_add(cpu, part, field, addnd)                       \
+       (per_cpu_ptr((part)->dkstats, (cpu))->field += (addnd))
 
 #define part_stat_read(part, field)                                    \
 ({                                                                     \
-       typeof(part->dkstats->field) res = 0;                           \
-       int i;                                                          \
-       for_each_possible_cpu(i)                                        \
-               res += per_cpu_ptr(part->dkstats, i)->field;            \
+       typeof((part)->dkstats->field) res = 0;                         \
+       unsigned int _cpu;                                              \
+       for_each_possible_cpu(_cpu)                                     \
+               res += per_cpu_ptr((part)->dkstats, _cpu)->field;       \
        res;                                                            \
 })
 
@@ -287,44 +313,50 @@ static inline void part_stat_set_all(struct hd_struct *part, int value)
                                sizeof(struct disk_stats));
 }
 
-#else /* !CONFIG_SMP */
-#define disk_stat_lock()       ({ rcu_read_lock(); 0; })
-#define disk_stat_unlock()     rcu_read_unlock()
-
-#define disk_stat_add(cpu, gendiskp, field, addnd)                     \
-       (gendiskp->dkstats.field += addnd)
-#define disk_stat_read(gendiskp, field)        (gendiskp->dkstats.field)
+static inline int init_part_stats(struct hd_struct *part)
+{
+       part->dkstats = alloc_percpu(struct disk_stats);
+       if (!part->dkstats)
+               return 0;
+       return 1;
+}
 
-static inline void disk_stat_set_all(struct gendisk *gendiskp, int value)
+static inline void free_part_stats(struct hd_struct *part)
 {
-       memset(&gendiskp->dkstats, value, sizeof (struct disk_stats));
+       free_percpu(part->dkstats);
 }
 
-#define part_stat_add(cpu, part, field, addnd)                         \
-       (part->dkstats.field += addnd)
+#else /* !CONFIG_SMP */
+#define part_stat_lock()       ({ rcu_read_lock(); 0; })
+#define part_stat_unlock()     rcu_read_unlock()
 
-#define all_stat_add(cpu, gendiskp, part, field, addnd, sector)                \
-({                                                                     \
-       if (part)                                                       \
-               part_stat_add(cpu, part, field, addnd);                 \
-       disk_stat_add(cpu, gendiskp, field, addnd);                     \
-})
+#define __part_stat_add(cpu, part, field, addnd)                               \
+       ((part)->dkstats.field += addnd)
 
-#define part_stat_read(part, field)    (part->dkstats.field)
+#define part_stat_read(part, field)    ((part)->dkstats.field)
 
 static inline void part_stat_set_all(struct hd_struct *part, int value)
 {
        memset(&part->dkstats, value, sizeof(struct disk_stats));
 }
 
+static inline int init_part_stats(struct hd_struct *part)
+{
+       return 1;
+}
+
+static inline void free_part_stats(struct hd_struct *part)
+{
+}
+
 #endif /* CONFIG_SMP */
 
-#define disk_stat_dec(cpu, gendiskp, field)                            \
-       disk_stat_add(cpu, gendiskp, field, -1)
-#define disk_stat_inc(cpu, gendiskp, field)                            \
-       disk_stat_add(cpu, gendiskp, field, 1)
-#define disk_stat_sub(cpu, gendiskp, field, subnd)                     \
-       disk_stat_add(cpu, gendiskp, field, -subnd)
+#define part_stat_add(cpu, part, field, addnd) do {                    \
+       __part_stat_add((cpu), (part), field, addnd);                   \
+       if ((part)->partno)                                             \
+               __part_stat_add((cpu), &part_to_disk((part))->part0,    \
+                               field, addnd);                          \
+} while (0)
 
 #define part_stat_dec(cpu, gendiskp, field)                            \
        part_stat_add(cpu, gendiskp, field, -1)
@@ -333,83 +365,67 @@ static inline void part_stat_set_all(struct hd_struct *part, int value)
 #define part_stat_sub(cpu, gendiskp, field, subnd)                     \
        part_stat_add(cpu, gendiskp, field, -subnd)
 
-#define all_stat_dec(cpu, gendiskp, field, sector)                     \
-       all_stat_add(cpu, gendiskp, field, -1, sector)
-#define all_stat_inc(cpu, gendiskp, part, field, sector)               \
-       all_stat_add(cpu, gendiskp, part, field, 1, sector)
-#define all_stat_sub(cpu, gendiskp, part, field, subnd, sector)                \
-       all_stat_add(cpu, gendiskp, part, field, -subnd, sector)
-
-/* Inlines to alloc and free disk stats in struct gendisk */
-#ifdef  CONFIG_SMP
-static inline int init_disk_stats(struct gendisk *disk)
-{
-       disk->dkstats = alloc_percpu(struct disk_stats);
-       if (!disk->dkstats)
-               return 0;
-       return 1;
-}
-
-static inline void free_disk_stats(struct gendisk *disk)
-{
-       free_percpu(disk->dkstats);
-}
-
-static inline int init_part_stats(struct hd_struct *part)
-{
-       part->dkstats = alloc_percpu(struct disk_stats);
-       if (!part->dkstats)
-               return 0;
-       return 1;
-}
-
-static inline void free_part_stats(struct hd_struct *part)
+static inline void part_inc_in_flight(struct hd_struct *part, int rw)
 {
-       free_percpu(part->dkstats);
+       atomic_inc(&part->in_flight[rw]);
+       if (part->partno)
+               atomic_inc(&part_to_disk(part)->part0.in_flight[rw]);
 }
 
-#else  /* CONFIG_SMP */
-static inline int init_disk_stats(struct gendisk *disk)
+static inline void part_dec_in_flight(struct hd_struct *part, int rw)
 {
-       return 1;
+       atomic_dec(&part->in_flight[rw]);
+       if (part->partno)
+               atomic_dec(&part_to_disk(part)->part0.in_flight[rw]);
 }
 
-static inline void free_disk_stats(struct gendisk *disk)
+static inline int part_in_flight(struct hd_struct *part)
 {
+       return atomic_read(&part->in_flight[0]) + atomic_read(&part->in_flight[1]);
 }
 
-static inline int init_part_stats(struct hd_struct *part)
+static inline struct partition_meta_info *alloc_part_info(struct gendisk *disk)
 {
-       return 1;
+       if (disk)
+               return kzalloc_node(sizeof(struct partition_meta_info),
+                                   GFP_KERNEL, disk->node_id);
+       return kzalloc(sizeof(struct partition_meta_info), GFP_KERNEL);
 }
 
-static inline void free_part_stats(struct hd_struct *part)
+static inline void free_part_info(struct hd_struct *part)
 {
+       kfree(part->info);
 }
-#endif /* CONFIG_SMP */
 
-/* drivers/block/ll_rw_blk.c */
-extern void disk_round_stats(int cpu, struct gendisk *disk);
+/* block/blk-core.c */
 extern void part_round_stats(int cpu, struct hd_struct *part);
 
-/* drivers/block/genhd.c */
-extern int get_blkdev_list(char *, int);
+/* block/genhd.c */
 extern void add_disk(struct gendisk *disk);
 extern void del_gendisk(struct gendisk *gp);
-extern void unlink_gendisk(struct gendisk *gp);
 extern struct gendisk *get_gendisk(dev_t dev, int *partno);
 extern struct block_device *bdget_disk(struct gendisk *disk, int partno);
 
 extern void set_device_ro(struct block_device *bdev, int flag);
 extern void set_disk_ro(struct gendisk *disk, int flag);
 
+static inline int get_disk_ro(struct gendisk *disk)
+{
+       return disk->part0.policy;
+}
+
+extern void disk_block_events(struct gendisk *disk);
+extern void disk_unblock_events(struct gendisk *disk);
+extern void disk_flush_events(struct gendisk *disk, unsigned int mask);
+extern unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask);
+
 /* drivers/char/random.c */
 extern void add_disk_randomness(struct gendisk *disk);
 extern void rand_initialize_disk(struct gendisk *disk);
 
 static inline sector_t get_start_sect(struct block_device *bdev)
 {
-       return bdev->bd_contains == bdev ? 0 : bdev->bd_part->start_sect;
+       return bdev->bd_part->start_sect;
 }
 static inline sector_t get_capacity(struct gendisk *disk)
 {
@@ -572,16 +588,20 @@ extern void blk_free_devt(dev_t devt);
 extern dev_t blk_lookup_devt(const char *name, int partno);
 extern char *disk_name (struct gendisk *hd, int partno, char *buf);
 
+extern int disk_expand_part_tbl(struct gendisk *disk, int target);
 extern int rescan_partitions(struct gendisk *disk, struct block_device *bdev);
-extern int __must_check add_partition(struct gendisk *, int, sector_t, sector_t, int);
+extern int invalidate_partitions(struct gendisk *disk, struct block_device *bdev);
+extern struct hd_struct * __must_check add_partition(struct gendisk *disk,
+                                                    int partno, sector_t start,
+                                                    sector_t len, int flags,
+                                                    struct partition_meta_info
+                                                      *info);
+extern void __delete_partition(struct hd_struct *);
 extern void delete_partition(struct gendisk *, int);
 extern void printk_all_partitions(void);
 
 extern struct gendisk *alloc_disk_node(int minors, int node_id);
 extern struct gendisk *alloc_disk(int minors);
-extern struct gendisk *alloc_disk_ext_node(int minors, int ext_minrs,
-                                          int node_id);
-extern struct gendisk *alloc_disk_ext(int minors, int ext_minors);
 extern struct kobject *get_disk(struct gendisk *disk);
 extern void put_disk(struct gendisk *disk);
 extern void blk_register_region(dev_t devt, unsigned long range,
@@ -593,6 +613,40 @@ extern void blk_unregister_region(dev_t devt, unsigned long range);
 
 extern ssize_t part_size_show(struct device *dev,
                              struct device_attribute *attr, char *buf);
+extern ssize_t part_stat_show(struct device *dev,
+                             struct device_attribute *attr, char *buf);
+extern ssize_t part_inflight_show(struct device *dev,
+                             struct device_attribute *attr, char *buf);
+#ifdef CONFIG_FAIL_MAKE_REQUEST
+extern ssize_t part_fail_show(struct device *dev,
+                             struct device_attribute *attr, char *buf);
+extern ssize_t part_fail_store(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count);
+#endif /* CONFIG_FAIL_MAKE_REQUEST */
+
+static inline void hd_ref_init(struct hd_struct *part)
+{
+       atomic_set(&part->ref, 1);
+       smp_mb();
+}
+
+static inline void hd_struct_get(struct hd_struct *part)
+{
+       atomic_inc(&part->ref);
+       smp_mb__after_atomic_inc();
+}
+
+static inline int hd_struct_try_get(struct hd_struct *part)
+{
+       return atomic_inc_not_zero(&part->ref);
+}
+
+static inline void hd_struct_put(struct hd_struct *part)
+{
+       if (atomic_dec_and_test(&part->ref))
+               __delete_partition(part);
+}
 
 #else /* CONFIG_BLOCK */