Commit b288f6ad authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] preparation to use of driverfs refcounts, part 2 - disk

	* disk->disk_dev is initialized in alloc_disk(), device_add()'d in
	  add_disk(), device_del()'d in unregister_disk() and device_put() in
	  put_disk().
	* devices of partitions are made its children.
	* attributes of disk one: dev (dev_t of the thing), range (number of
	  minors) and size (in sectors).
	* attributes of partition ones: dev (ditto), start (in sectors) and
	  size (in sectors).
	* disk devices are put on a new bus - "block"
	* if caller of add_disk() had set disk->driverfs_dev, we set symlinks:
	  "device" from disk to underlying device and "block" from underlying
	  device to disk.
	* ->release() of disk_dev frees disk and disk->part.
	At that point we have sane driverfs subtree for each gendisk and
refcount of its root (disk->disk_dev) can act as gendisk refcount.
parent afae25b7
......@@ -192,6 +192,10 @@ struct device_class disk_devclass = {
.name = "disk",
};
static struct bus_type disk_bus = {
name: "block",
};
int __init device_init(void)
{
int i;
......@@ -200,6 +204,7 @@ int __init device_init(void)
INIT_LIST_HEAD(&gendisks[i].list);
blk_dev_init();
devclass_register(&disk_devclass);
bus_register(&disk_bus);
return 0;
}
......@@ -207,6 +212,13 @@ __initcall(device_init);
EXPORT_SYMBOL(disk_devclass);
static void disk_release(struct device *dev)
{
struct gendisk *disk = dev->driver_data;
kfree(disk->part);
kfree(disk);
}
struct gendisk *alloc_disk(int minors)
{
struct gendisk *disk = kmalloc(sizeof(struct gendisk), GFP_KERNEL);
......@@ -224,16 +236,19 @@ struct gendisk *alloc_disk(int minors)
disk->minors = minors;
while (minors >>= 1)
disk->minor_shift++;
disk->disk_dev.bus = &disk_bus;
disk->disk_dev.release = disk_release;
disk->disk_dev.driver_data = disk;
device_initialize(&disk->disk_dev);
}
return disk;
}
void put_disk(struct gendisk *disk)
{
if (disk) {
kfree(disk->part);
kfree(disk);
}
if (disk)
put_device(&disk->disk_dev);
}
EXPORT_SYMBOL(alloc_disk);
EXPORT_SYMBOL(put_disk);
......@@ -3196,6 +3196,7 @@ static int ide_cdrom_attach (ide_drive_t *drive)
g->minors = 1;
g->minor_shift = 0;
g->de = drive->de;
g->driverfs_dev = &drive->gendev;
g->flags = GENHD_FL_CD;
if (ide_cdrom_setup(drive)) {
struct cdrom_device_info *devinfo = &info->devinfo;
......
......@@ -1874,6 +1874,7 @@ static int idedisk_attach(ide_drive_t *drive)
g->minors = 1 << PARTN_BITS;
g->minor_shift = PARTN_BITS;
g->de = drive->de;
g->driverfs_dev = &drive->gendev;
g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0;
g->flags |= GENHD_FL_DEVFS;
set_capacity(g, current_capacity(drive));
......
......@@ -2110,6 +2110,7 @@ static int idefloppy_attach (ide_drive_t *drive)
DRIVER(drive)->busy--;
g->minors = 1 << PARTN_BITS;
g->minor_shift = PARTN_BITS;
g->driverfs_dev = &drive->gendev;
g->de = drive->de;
g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0;
g->flags |= GENHD_FL_DEVFS;
......
......@@ -726,24 +726,6 @@ static int sr_init()
return 1;
}
/* Driverfs file support */
static ssize_t sr_device_kdev_read(struct device *driverfs_dev,
char *page, size_t count, loff_t off)
{
kdev_t kdev;
kdev.value=(int)(long)driverfs_dev->driver_data;
return off ? 0 : sprintf(page, "%x\n",kdev.value);
}
static DEVICE_ATTR(kdev,S_IRUGO,sr_device_kdev_read,NULL);
static ssize_t sr_device_type_read(struct device *driverfs_dev,
char *page, size_t count, loff_t off)
{
return off ? 0 : sprintf (page, "CHR\n");
}
static DEVICE_ATTR(type,S_IRUGO,sr_device_type_read,NULL);
void sr_finish()
{
int i;
......@@ -797,22 +779,8 @@ void sr_finish()
*/
get_capabilities(cd);
sr_vendor_init(cd);
sprintf(cd->cdi.cdrom_driverfs_dev.bus_id, "%s:cd",
cd->device->sdev_driverfs_dev.bus_id);
sprintf(cd->cdi.cdrom_driverfs_dev.name, "%scdrom",
cd->device->sdev_driverfs_dev.name);
cd->cdi.cdrom_driverfs_dev.parent =
&cd->device->sdev_driverfs_dev;
cd->cdi.cdrom_driverfs_dev.bus = &scsi_driverfs_bus_type;
cd->cdi.cdrom_driverfs_dev.driver_data =
(void *)(long)__mkdev(MAJOR_NR, i);
device_register(&cd->cdi.cdrom_driverfs_dev);
device_create_file(&cd->cdi.cdrom_driverfs_dev,
&dev_attr_type);
device_create_file(&cd->cdi.cdrom_driverfs_dev,
&dev_attr_kdev);
disk->de = cd->device->de;
disk->driverfs_dev = &cd->device->sdev_driverfs_dev;
register_cdrom(&cd->cdi);
set_capacity(disk, cd->capacity);
add_disk(disk);
......
......@@ -18,6 +18,7 @@
#include <linux/blk.h>
#include <linux/kmod.h>
#include <linux/ctype.h>
#include <../drivers/base/fs/fs.h> /* Eeeeewwwww */
#include "check.h"
......@@ -111,57 +112,6 @@ char *disk_name(struct gendisk *hd, int part, char *buf)
return buf;
}
/* Driverfs file support */
static ssize_t partition_device_kdev_read(struct device *driverfs_dev,
char *page, size_t count, loff_t off)
{
kdev_t kdev;
kdev.value=(int)(long)driverfs_dev->driver_data;
return off ? 0 : sprintf (page, "%x\n",kdev.value);
}
static DEVICE_ATTR(kdev,S_IRUGO,partition_device_kdev_read,NULL);
static ssize_t partition_device_type_read(struct device *driverfs_dev,
char *page, size_t count, loff_t off)
{
return off ? 0 : sprintf (page, "BLK\n");
}
static DEVICE_ATTR(type,S_IRUGO,partition_device_type_read,NULL);
static void driverfs_create_partitions(struct gendisk *hd)
{
struct device *parent = hd->driverfs_dev;
struct device *dev = &hd->disk_dev;
/* if driverfs not supported by subsystem, skip partitions */
if (!(hd->flags & GENHD_FL_DRIVERFS))
return;
if (parent) {
sprintf(dev->name, "%sdisc", parent->name);
sprintf(dev->bus_id, "%sdisc", parent->bus_id);
dev->parent = parent;
dev->bus = parent->bus;
} else {
sprintf(dev->name, "disc");
sprintf(dev->bus_id, "disc");
}
dev->driver_data = (void *)(long)__mkdev(hd->major, hd->first_minor);
device_register(dev);
device_create_file(dev, &dev_attr_type);
device_create_file(dev, &dev_attr_kdev);
}
static void driverfs_remove_partitions(struct gendisk *hd)
{
struct device *dev = &hd->disk_dev;
if (!(hd->flags & GENHD_FL_DRIVERFS))
return;
device_remove_file(dev, &dev_attr_type);
device_remove_file(dev, &dev_attr_kdev);
put_device(dev);
}
static struct parsed_partitions *
check_partition(struct gendisk *hd, struct block_device *bdev)
{
......@@ -326,6 +276,40 @@ static void devfs_remove_partitions(struct gendisk *dev)
#endif
}
static ssize_t part_dev_read(struct device *dev,
char *page, size_t count, loff_t off)
{
struct gendisk *disk = dev->parent->driver_data;
struct hd_struct *p = dev->driver_data;
int part = p - disk->part + 1;
dev_t base = MKDEV(disk->major, disk->first_minor);
return off ? 0 : sprintf(page, "%04x\n",base + part);
}
static ssize_t part_start_read(struct device *dev,
char *page, size_t count, loff_t off)
{
struct hd_struct *p = dev->driver_data;
return off ? 0 : sprintf(page, "%llu\n",(u64)p->start_sect);
}
static ssize_t part_size_read(struct device *dev,
char *page, size_t count, loff_t off)
{
struct hd_struct *p = dev->driver_data;
return off ? 0 : sprintf(page, "%llu\n",(u64)p->nr_sects);
}
static struct device_attribute part_attr_dev = {
.attr = {.name = "dev", .mode = S_IRUGO },
.show = part_dev_read
};
static struct device_attribute part_attr_start = {
.attr = {.name = "start", .mode = S_IRUGO },
.show = part_start_read
};
static struct device_attribute part_attr_size = {
.attr = {.name = "size", .mode = S_IRUGO },
.show = part_size_read
};
void delete_partition(struct gendisk *disk, int part)
{
struct hd_struct *p = disk->part + part - 1;
......@@ -338,8 +322,9 @@ void delete_partition(struct gendisk *disk, int part)
dev = p->hd_driverfs_dev;
p->hd_driverfs_dev = NULL;
if (dev) {
device_remove_file(dev, &dev_attr_type);
device_remove_file(dev, &dev_attr_kdev);
device_remove_file(dev, &part_attr_size);
device_remove_file(dev, &part_attr_start);
device_remove_file(dev, &part_attr_dev);
device_unregister(dev);
}
}
......@@ -352,43 +337,130 @@ static void part_release(struct device *dev)
void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len)
{
struct hd_struct *p = disk->part + part - 1;
struct device *parent = disk->disk_dev.parent;
struct device *parent = &disk->disk_dev;
struct device *dev;
p->start_sect = start;
p->nr_sects = len;
devfs_register_partition(disk, part);
if (!(disk->flags & GENHD_FL_DRIVERFS))
return;
dev = kmalloc(sizeof(struct device), GFP_KERNEL);
if (!dev)
return;
memset(dev, 0, sizeof(struct device));
if (parent) {
sprintf(dev->name, "%spart%d", parent->name, part);
sprintf(dev->bus_id, "%s:p%d", parent->bus_id, part);
dev->parent = parent;
dev->bus = parent->bus;
} else {
sprintf(dev->name, "part%d", part);
sprintf(dev->bus_id, "p%d", part);
}
dev->parent = parent;
sprintf(dev->bus_id, "p%d", part);
dev->release = part_release;
dev->driver_data =
(void *)(long)__mkdev(disk->major, disk->first_minor+part);
dev->driver_data = p;
device_register(dev);
device_create_file(dev, &dev_attr_type);
device_create_file(dev, &dev_attr_kdev);
device_create_file(dev, &part_attr_dev);
device_create_file(dev, &part_attr_start);
device_create_file(dev, &part_attr_size);
p->hd_driverfs_dev = dev;
}
static ssize_t disk_dev_read(struct device *dev,
char *page, size_t count, loff_t off)
{
struct gendisk *disk = dev->driver_data;
dev_t base = MKDEV(disk->major, disk->first_minor);
return off ? 0 : sprintf(page, "%04x\n",base);
}
static ssize_t disk_range_read(struct device *dev,
char *page, size_t count, loff_t off)
{
struct gendisk *disk = dev->driver_data;
return off ? 0 : sprintf(page, "%d\n",disk->minors);
}
static ssize_t disk_size_read(struct device *dev,
char *page, size_t count, loff_t off)
{
struct gendisk *disk = dev->driver_data;
return off ? 0 : sprintf(page, "%llu\n",(u64)get_capacity(disk));
}
static struct device_attribute disk_attr_dev = {
.attr = {.name = "dev", .mode = S_IRUGO },
.show = disk_dev_read
};
static struct device_attribute disk_attr_range = {
.attr = {.name = "range", .mode = S_IRUGO },
.show = disk_range_read
};
static struct device_attribute disk_attr_size = {
.attr = {.name = "size", .mode = S_IRUGO },
.show = disk_size_read
};
static void disk_driverfs_symlinks(struct gendisk *disk)
{
struct device *target = disk->driverfs_dev;
struct device *dev = &disk->disk_dev;
struct device *p;
char *path;
char *s;
int length;
int depth;
if (!target)
return;
get_device(target);
length = get_devpath_length(target);
length += strlen("..");
if (length > PATH_MAX)
return;
if (!(path = kmalloc(length,GFP_KERNEL)))
return;
memset(path,0,length);
/* our relative position */
strcpy(path,"..");
fill_devpath(target, path, length);
driverfs_create_symlink(&dev->dir, "device", path);
kfree(path);
for (p = target, depth = 0; p; p = p->parent, depth++)
;
length = get_devpath_length(dev);
length += 3 * depth - 1;
if (length > PATH_MAX)
return;
if (!(path = kmalloc(length,GFP_KERNEL)))
return;
memset(path,0,length);
for (s = path; depth--; s += 3)
strcpy(s, "../");
fill_devpath(dev, path, length);
driverfs_create_symlink(&target->dir, "block", path);
kfree(path);
}
/* Not exported, helper to add_disk(). */
void register_disk(struct gendisk *disk)
{
struct device *dev = &disk->disk_dev;
struct parsed_partitions *state;
struct block_device *bdev;
char *s;
int j;
strcpy(dev->bus_id, disk->disk_name);
/* ewww... some of these buggers have / in name... */
s = strchr(dev->bus_id, '/');
if (s)
*s = '!';
device_add(dev);
device_create_file(dev, &disk_attr_dev);
device_create_file(dev, &disk_attr_range);
device_create_file(dev, &disk_attr_size);
disk_driverfs_symlinks(disk);
if (disk->flags & GENHD_FL_CD)
devfs_create_cdrom(disk);
......@@ -404,7 +476,6 @@ void register_disk(struct gendisk *disk)
if (blkdev_get(bdev, FMODE_READ, 0, BDEV_RAW) < 0)
return;
state = check_partition(disk, bdev);
driverfs_create_partitions(disk);
devfs_create_partitions(disk);
if (state) {
for (j = 1; j < state->limit; j++) {
......@@ -499,8 +570,16 @@ void del_gendisk(struct gendisk *disk)
disk->capacity = 0;
disk->flags &= ~GENHD_FL_UP;
unlink_gendisk(disk);
driverfs_remove_partitions(disk);
devfs_remove_partitions(disk);
device_remove_file(&disk->disk_dev, &disk_attr_dev);
device_remove_file(&disk->disk_dev, &disk_attr_range);
device_remove_file(&disk->disk_dev, &disk_attr_size);
driverfs_remove_file(&disk->disk_dev.dir, "device");
if (disk->driverfs_dev) {
driverfs_remove_file(&disk->driverfs_dev->dir, "block");
put_device(disk->driverfs_dev);
}
device_del(&disk->disk_dev);
}
struct dev_name {
......
......@@ -730,7 +730,6 @@ struct cdrom_device_info {
struct cdrom_device_ops *ops; /* link to device_ops */
struct cdrom_device_info *next; /* next device_info for this major */
void *handle; /* driver-dependent data */
struct device cdrom_driverfs_dev; /* driverfs implementation */
/* specifications */
kdev_t dev; /* device number */
int mask; /* mask of capability: disables them */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment