Commit b54cee93 authored by Linus Torvalds's avatar Linus Torvalds

v2.5.0.6 -> v2.5.0.7

- Jens Axboe: more bio fixes/cleanups/breakage ;)
- Al Viro: superblock cleanups, boot/root mounting.
parent ef40d49b
VERSION = 2
PATCHLEVEL = 5
SUBLEVEL = 1
EXTRAVERSION =-pre6
EXTRAVERSION =-pre7
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
......@@ -257,8 +257,8 @@ Version: dummy
boot: vmlinux
@$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C arch/$(ARCH)/boot
vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \
vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o init/do_mounts.o linuxsubdirs
$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o init/do_mounts.o \
--start-group \
$(CORE_FILES) \
$(DRIVERS) \
......@@ -335,6 +335,9 @@ init/version.o: init/version.c include/linux/compile.h include/config/MARKER
init/main.o: init/main.c include/config/MARKER
$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -c -o $*.o $<
init/do_mounts.o: init/do_mounts.c include/config/MARKER
$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -c -o $*.o $<
fs lib mm ipc kernel drivers net: dummy
$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@)
......
......@@ -1114,17 +1114,26 @@ static void __devinit pci_fixup_piix4_acpi(struct pci_dev *d)
* But it does seem to fix some unspecified problem
* with 'movntq' copies on Athlons.
*
* VIA 8363 chipset:
* - bit 7 at offset 0x55: Debug (RW)
* VIA 8363,8622,8361 Northbridges:
* - bits 5, 6, 7 at offset 0x55 need to be turned off
* VIA 8367 (KT266x) Northbridges:
* - bits 5, 6, 7 at offset 0x95 need to be turned off
*/
static void __init pci_fixup_via_athlon_bug(struct pci_dev *d)
{
u8 v;
pci_read_config_byte(d, 0x55, &v);
if (v & 0x80) {
int where = 0x55;
if (d->device == PCI_DEVICE_ID_VIA_8367_0) {
where = 0x95; /* the memory write queue timer register is
different for the kt266x's: 0x95 not 0x55 */
}
pci_read_config_byte(d, where, &v);
if (v & 0xe0) {
printk("Trying to stomp on Athlon bug...\n");
v &= 0x7f; /* clear bit 55.7 */
pci_write_config_byte(d, 0x55, v);
v &= 0x1f; /* clear bits 5, 6, 7 */
pci_write_config_byte(d, where, v);
}
}
......@@ -1138,6 +1147,9 @@ struct pci_fixup pcibios_fixups[] = {
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5598, pci_fixup_latency },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, pci_fixup_piix4_acpi },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8363_0, pci_fixup_via_athlon_bug },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8622, pci_fixup_via_athlon_bug },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8361, pci_fixup_via_athlon_bug },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8367_0, pci_fixup_via_athlon_bug },
{ 0 }
};
......
......@@ -60,6 +60,12 @@ inline int bio_rq_in_between(struct bio *bio, struct request *rq,
BUG_ON(next_rq->flags & REQ_STARTED);
/*
* not a sector based request
*/
if (!(next_rq->flags & REQ_CMD))
return 0;
/*
* if the device is different (not a normal case) just check if
* bio is after rq
......@@ -95,6 +101,9 @@ inline int bio_rq_in_between(struct bio *bio, struct request *rq,
*/
inline int elv_rq_merge_ok(struct request *rq, struct bio *bio)
{
if (!(rq->flags & REQ_CMD))
return 0;
/*
* different data direction or already started, don't merge
*/
......@@ -133,6 +142,8 @@ int elevator_linus_merge(request_queue_t *q, struct request **req,
break;
if (__rq->flags & (REQ_BARRIER | REQ_STARTED))
break;
if (!(__rq->flags & REQ_CMD))
continue;
if (!*req && bio_rq_in_between(bio, __rq, &q->queue_head))
*req = __rq;
......@@ -226,6 +237,9 @@ int elevator_noop_merge(request_queue_t *q, struct request **req,
if (__rq->flags & (REQ_BARRIER | REQ_STARTED))
break;
if (!(__rq->flags & REQ_CMD))
continue;
if (!elv_rq_merge_ok(__rq, bio))
continue;
......
......@@ -3914,7 +3914,7 @@ static void __init register_devfs_entries (int drive)
{NULL, t360, t1200, t3in+5+8, t3in+5, t3in, t3in};
base_minor = (drive < 4) ? drive : (124 + drive);
if (UDP->cmos <= NUMBER(default_drive_params)) {
if (UDP->cmos < NUMBER(default_drive_params)) {
i = 0;
do {
char name[16];
......
......@@ -291,13 +291,13 @@ static int ll_10byte_cmd_build(request_queue_t *q, struct request *rq)
if (!(rq->flags & REQ_CMD))
return 0;
memset(rq->cmd, 0, sizeof(rq->cmd));
if (rq_data_dir(rq) == READ)
rq->cmd[0] = READ_10;
else
rq->cmd[0] = WRITE_10;
rq->cmd[1] = 0;
/*
* fill in lba
*/
......@@ -305,7 +305,6 @@ static int ll_10byte_cmd_build(request_queue_t *q, struct request *rq)
rq->cmd[3] = (block >> 16) & 0xff;
rq->cmd[4] = (block >> 8) & 0xff;
rq->cmd[5] = block & 0xff;
rq->cmd[6] = 0;
/*
* and transfer length
......@@ -347,7 +346,7 @@ int blk_rq_map_sg(request_queue_t *q, struct request *rq, struct scatterlist *sg
unsigned long long lastend;
struct bio_vec *bvec;
struct bio *bio;
int nsegs, i, cluster, j;
int nsegs, i, cluster;
nsegs = 0;
bio = rq->bio;
......@@ -357,9 +356,7 @@ int blk_rq_map_sg(request_queue_t *q, struct request *rq, struct scatterlist *sg
/*
* for each bio in rq
*/
j = 0;
rq_for_each_bio(bio, rq) {
j++;
/*
* for each segment in bio
*/
......@@ -386,7 +383,6 @@ int blk_rq_map_sg(request_queue_t *q, struct request *rq, struct scatterlist *sg
new_segment:
if (nsegs > q->max_segments) {
printk("map: %d >= %d\n", nsegs, q->max_segments);
printk("map %d, %d, bio_sectors %d, vcnt %d\n", i, j, bio_sectors(bio), bio->bi_vcnt);
BUG();
}
......@@ -411,7 +407,7 @@ int blk_rq_map_sg(request_queue_t *q, struct request *rq, struct scatterlist *sg
static inline int ll_new_segment(request_queue_t *q, struct request *req,
struct bio *bio)
{
if (req->nr_segments + bio->bi_vcnt < q->max_segments) {
if (req->nr_segments + bio->bi_vcnt <= q->max_segments) {
req->nr_segments += bio->bi_vcnt;
return 1;
}
......@@ -480,9 +476,17 @@ void blk_plug_device(request_queue_t *q)
*/
static inline void __generic_unplug_device(request_queue_t *q)
{
if (test_and_clear_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags))
if (!elv_queue_empty(q))
q->request_fn(q);
/*
* not plugged
*/
if (!test_and_clear_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags))
return;
/*
* was plugged, fire request_fn if queue has stuff to do
*/
if (!elv_queue_empty(q))
q->request_fn(q);
}
/**
......
......@@ -1090,6 +1090,7 @@ static int open_aux(struct inode * inode, struct file * file)
spin_unlock_irqrestore(&aux_count_lock, flags);
return -EBUSY;
}
spin_unlock_irqrestore(&aux_count_lock, flags);
kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable the
auxiliary port on
controller. */
......@@ -1099,7 +1100,6 @@ static int open_aux(struct inode * inode, struct file * file)
mdelay(2); /* Ensure we follow the kbc access delay rules.. */
send_data(KBD_CMD_ENABLE); /* try to workaround toshiba4030cdt problem */
spin_unlock_irqrestore(&aux_count_lock, flags);
return 0;
}
......
......@@ -377,7 +377,7 @@ struct bio *bio_copy(struct bio *bio, int gfp_mask, int copy)
/*
* iterate iovec list and alloc pages + copy data
*/
bio_for_each_segment(bv, bio, i) {
__bio_for_each_segment(bv, bio, i, 0) {
struct bio_vec *bbv = &b->bi_io_vec[i];
char *vfrom, *vto;
......@@ -392,8 +392,7 @@ struct bio *bio_copy(struct bio *bio, int gfp_mask, int copy)
vfrom = kmap(bv->bv_page);
vto = kmap(bbv->bv_page);
} else {
__save_flags(flags);
__cli();
local_irq_save(flags);
vfrom = kmap_atomic(bv->bv_page, KM_BIO_IRQ);
vto = kmap_atomic(bbv->bv_page, KM_BIO_IRQ);
}
......@@ -405,7 +404,7 @@ struct bio *bio_copy(struct bio *bio, int gfp_mask, int copy)
} else {
kunmap_atomic(vto, KM_BIO_IRQ);
kunmap_atomic(vfrom, KM_BIO_IRQ);
__restore_flags(flags);
local_irq_restore(flags);
}
fill_in:
......@@ -424,10 +423,8 @@ struct bio *bio_copy(struct bio *bio, int gfp_mask, int copy)
return b;
oom:
while (i >= 0) {
while (--i >= 0)
__free_page(b->bi_io_vec[i].bv_page);
i--;
}
bio_pool_put(b);
return NULL;
......@@ -613,6 +610,11 @@ void ll_rw_kio(int rw, struct kiobuf *kio, kdev_t dev, sector_t sector)
if (err)
kio->errno = err;
/*
* final atomic_dec of io_count to match our initial setting of 1.
* I/O may or may not have completed at this point, final completion
* handler is only run on last decrement.
*/
end_kio_request(kio, !err);
}
......
......@@ -15,12 +15,10 @@
#include <linux/quotaops.h>
#include <linux/acct.h>
#include <linux/module.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
#include <linux/seq_file.h>
struct vfsmount *do_kern_mount(char *type, int flags, char *name, void *data);
int do_remount_sb(struct super_block *sb, int flags, void * data);
void kill_super(struct super_block *sb);
......@@ -31,9 +29,7 @@ static kmem_cache_t *mnt_cache;
static LIST_HEAD(vfsmntlist);
static DECLARE_MUTEX(mount_sem);
/* Will be static */
struct vfsmount *root_vfsmnt;
static struct vfsmount *root_vfsmnt;
static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry)
{
......@@ -462,8 +458,7 @@ static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry)
return NULL;
}
/* Will become static */
int graft_tree(struct vfsmount *mnt, struct nameidata *nd)
static int graft_tree(struct vfsmount *mnt, struct nameidata *nd)
{
int err;
if (mnt->mnt_sb->s_flags & MS_NOUSER)
......@@ -928,6 +923,44 @@ asmlinkage long sys_pivot_root(const char *new_root, const char *put_old)
* In 2.5 we'll use ramfs or tmpfs, but for now it's all we need - just
* something to go with root vfsmount.
*/
static struct inode_operations rootfs_dir_inode_operations;
static struct file_operations rootfs_dir_operations;
static int rootfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
struct inode * inode = new_inode(dir->i_sb);
int error = -ENOSPC;
if (inode) {
inode->i_mode = S_IFDIR|mode;
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
inode->i_op = &rootfs_dir_inode_operations;
inode->i_fop = &rootfs_dir_operations;
d_instantiate(dentry, inode);
dget(dentry);
error = 0;
}
return error;
}
static int rootfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev)
{
struct inode * inode = new_inode(dir->i_sb);
int error = -ENOSPC;
if (inode) {
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
init_special_inode(inode, mode, dev);
d_instantiate(dentry, inode);
dget(dentry);
error = 0;
}
return error;
}
static int rootfs_unlink(struct inode * dir, struct dentry *dentry)
{
dentry->d_inode->i_nlink--;
dput(dentry);
return 0;
}
static struct dentry *rootfs_lookup(struct inode *dir, struct dentry *dentry)
{
d_add(dentry, NULL);
......@@ -939,6 +972,9 @@ static struct file_operations rootfs_dir_operations = {
};
static struct inode_operations rootfs_dir_inode_operations = {
lookup: rootfs_lookup,
mkdir: rootfs_mkdir,
mknod: rootfs_mknod,
unlink: rootfs_unlink,
};
static struct super_block *rootfs_read_super(struct super_block * sb, void * data, int silent)
{
......@@ -1030,93 +1066,3 @@ void __init mnt_init(unsigned long mempages)
} while (i);
init_mount_tree();
}
#ifdef CONFIG_BLK_DEV_INITRD
int __init change_root(kdev_t new_root_dev,const char *put_old)
{
struct vfsmount *old_rootmnt;
struct nameidata devfs_nd, nd;
struct nameidata parent_nd;
char *new_devname = kmalloc(strlen("/dev/root.old")+1, GFP_KERNEL);
int error = 0;
if (new_devname)
strcpy(new_devname, "/dev/root.old");
read_lock(&current->fs->lock);
old_rootmnt = mntget(current->fs->rootmnt);
read_unlock(&current->fs->lock);
/* First unmount devfs if mounted */
if (path_init("/dev", LOOKUP_FOLLOW|LOOKUP_POSITIVE, &devfs_nd))
error = path_walk("/dev", &devfs_nd);
if (!error) {
if (devfs_nd.mnt->mnt_sb->s_magic == DEVFS_SUPER_MAGIC &&
devfs_nd.dentry == devfs_nd.mnt->mnt_root) {
do_umount(devfs_nd.mnt, 0);
}
path_release(&devfs_nd);
}
set_fs_pwd(current->fs, root_vfsmnt, root_vfsmnt->mnt_root);
set_fs_root(current->fs, root_vfsmnt, root_vfsmnt->mnt_root);
spin_lock(&dcache_lock);
detach_mnt(old_rootmnt, &parent_nd);
spin_unlock(&dcache_lock);
ROOT_DEV = new_root_dev;
mount_root();
#if 1
shrink_dcache();
printk("change_root: old root has d_count=%d\n",
atomic_read(&old_rootmnt->mnt_root->d_count));
#endif
mount_devfs_fs ();
/*
* Get the new mount directory
*/
error = 0;
if (path_init(put_old, LOOKUP_FOLLOW|LOOKUP_POSITIVE|LOOKUP_DIRECTORY, &nd))
error = path_walk(put_old, &nd);
if (error) {
int blivet;
struct block_device *ramdisk = old_rootmnt->mnt_sb->s_bdev;
atomic_inc(&ramdisk->bd_count);
blivet = blkdev_get(ramdisk, FMODE_READ, 0, BDEV_FS);
printk(KERN_NOTICE "Trying to unmount old root ... ");
if (!blivet) {
spin_lock(&dcache_lock);
list_del_init(&old_rootmnt->mnt_list);
spin_unlock(&dcache_lock);
mntput(old_rootmnt);
mntput(old_rootmnt);
blivet = ioctl_by_bdev(ramdisk, BLKFLSBUF, 0);
path_release(&parent_nd);
blkdev_put(ramdisk, BDEV_FS);
}
if (blivet) {
printk(KERN_ERR "error %d\n", blivet);
} else {
printk("okay\n");
error = 0;
}
kfree(new_devname);
return error;
}
spin_lock(&dcache_lock);
attach_mnt(old_rootmnt, &nd);
if (new_devname) {
if (old_rootmnt->mnt_devname)
kfree(old_rootmnt->mnt_devname);
old_rootmnt->mnt_devname = new_devname;
}
spin_unlock(&dcache_lock);
/* put the old stuff */
path_release(&parent_nd);
mntput(old_rootmnt);
path_release(&nd);
return 0;
}
#endif
......@@ -21,36 +21,21 @@
*/
#include <linux/config.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/locks.h>
#include <linux/smp_lock.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/fd.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/quotaops.h>
#include <linux/acct.h>
#include <asm/uaccess.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_fs_sb.h>
#include <linux/nfs_mount.h>
#include <linux/kmod.h>
#define __NO_VERSION__
#include <linux/module.h>
extern void wait_for_keypress(void);
extern int root_mountflags;
int do_remount_sb(struct super_block *sb, int flags, void * data);
/* this is initialized in init/main.c */
kdev_t ROOT_DEV;
LIST_HEAD(super_blocks);
spinlock_t sb_lock = SPIN_LOCK_UNLOCKED;
......@@ -429,10 +414,6 @@ struct vfsmount *alloc_vfsmnt(void);
void free_vfsmnt(struct vfsmount *mnt);
void set_devname(struct vfsmount *mnt, const char *name);
/* Will go away */
extern struct vfsmount *root_vfsmnt;
extern int graft_tree(struct vfsmount *mnt, struct nameidata *nd);
static inline struct super_block * find_super(kdev_t dev)
{
struct list_head *p;
......@@ -549,37 +530,6 @@ asmlinkage long sys_ustat(dev_t dev, struct ustat * ubuf)
return err;
}
static struct super_block * read_super(kdev_t dev, struct block_device *bdev,
struct file_system_type *type, int flags,
void *data)
{
struct super_block * s;
s = alloc_super();
if (!s)
goto out;
s->s_dev = dev;
s->s_bdev = bdev;
s->s_flags = flags;
spin_lock(&sb_lock);
insert_super(s, type);
lock_super(s);
if (!type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0))
goto out_fail;
s->s_flags |= MS_ACTIVE;
unlock_super(s);
/* tell bdcache that we are going to keep this one */
if (bdev)
atomic_inc(&bdev->bd_count);
out:
return s;
out_fail:
unlock_super(s);
deactivate_super(s);
remove_super(s);
return NULL;
}
/*
* Unnamed block devices are dummy devices used by virtual
* filesystems which don't use real block-devices. -- jrs
......@@ -708,17 +658,30 @@ static struct super_block *get_sb_bdev(struct file_system_type *fs_type,
static struct super_block *get_sb_nodev(struct file_system_type *fs_type,
int flags, void * data)
{
kdev_t dev;
int error = -EMFILE;
dev = get_unnamed_dev();
if (dev) {
struct super_block * sb;
error = -EINVAL;
sb = read_super(dev, NULL, fs_type, flags, data);
if (sb)
return sb;
struct super_block *s = alloc_super();
if (!s)
return ERR_PTR(-ENOMEM);
s->s_dev = get_unnamed_dev();
if (!s->s_dev) {
destroy_super(s);
return ERR_PTR(-EMFILE);
}
return ERR_PTR(error);
s->s_flags = flags;
spin_lock(&sb_lock);
insert_super(s, fs_type);
lock_super(s);
if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0))
goto out_fail;
s->s_flags |= MS_ACTIVE;
unlock_super(s);
return s;
out_fail:
unlock_super(s);
deactivate_super(s);
remove_super(s);
return ERR_PTR(-EINVAL);
}
static struct super_block *get_sb_single(struct file_system_type *fs_type,
......@@ -743,13 +706,12 @@ static struct super_block *get_sb_single(struct file_system_type *fs_type,
do_remount_sb(old, flags, data);
return old;
} else {
kdev_t dev = get_unnamed_dev();
if (!dev) {
s->s_dev = get_unnamed_dev();
if (!s->s_dev) {
spin_unlock(&sb_lock);
destroy_super(s);
return ERR_PTR(-EMFILE);
}
s->s_dev = dev;
s->s_flags = flags;
insert_super(s, fs_type);
lock_super(s);
......@@ -899,212 +861,3 @@ struct vfsmount *kern_mount(struct file_system_type *type)
{
return do_kern_mount((char *)type->name, 0, (char *)type->name, NULL);
}
static char * __initdata root_mount_data;
static int __init root_data_setup(char *str)
{
root_mount_data = str;
return 1;
}
static char * __initdata root_fs_names;
static int __init fs_names_setup(char *str)
{
root_fs_names = str;
return 1;
}
__setup("rootflags=", root_data_setup);
__setup("rootfstype=", fs_names_setup);
static void __init get_fs_names(char *page)
{
char *s = page;
if (root_fs_names) {
strcpy(page, root_fs_names);
while (*s++) {
if (s[-1] == ',')
s[-1] = '\0';
}
} else {
int len = get_filesystem_list(page);
char *p, *next;
page[len] = '\0';
for (p = page-1; p; p = next) {
next = strchr(++p, '\n');
if (*p++ != '\t')
continue;
while ((*s++ = *p++) != '\n')
;
s[-1] = '\0';
}
}
*s = '\0';
}
void __init mount_root(void)
{
struct nameidata root_nd;
struct super_block * sb;
struct vfsmount *vfsmnt;
struct block_device *bdev = NULL;
mode_t mode;
int retval;
void *handle;
char path[64];
char *name = "/dev/root";
char *fs_names, *p;
#ifdef CONFIG_ROOT_NFS
void *data;
#endif
root_mountflags |= MS_VERBOSE;
#ifdef CONFIG_ROOT_NFS
if (MAJOR(ROOT_DEV) != UNNAMED_MAJOR)
goto skip_nfs;
data = nfs_root_data();
if (!data)
goto no_nfs;
vfsmnt = do_kern_mount("nfs", root_mountflags, "/dev/root", data);
if (!IS_ERR(vfsmnt)) {
printk ("VFS: Mounted root (%s filesystem).\n", "nfs");
ROOT_DEV = vfsmnt->mnt_sb->s_dev;
goto attach_it;
}
no_nfs:
printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0);
skip_nfs:
#endif
#ifdef CONFIG_BLK_DEV_FD
if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
#ifdef CONFIG_BLK_DEV_RAM
extern int rd_doload;
extern void rd_load_secondary(void);
#endif
floppy_eject();
#ifndef CONFIG_BLK_DEV_RAM
printk(KERN_NOTICE "(Warning, this kernel has no ramdisk support)\n");
#else
/* rd_doload is 2 for a dual initrd/ramload setup */
if(rd_doload==2)
rd_load_secondary();
else
#endif
{
printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER\n");
wait_for_keypress();
}
}
#endif
fs_names = __getname();
get_fs_names(fs_names);
devfs_make_root (root_device_name);
handle = devfs_find_handle (NULL, ROOT_DEVICE_NAME,
MAJOR (ROOT_DEV), MINOR (ROOT_DEV),
DEVFS_SPECIAL_BLK, 1);
if (handle) {
int n;
unsigned major, minor;
devfs_get_maj_min (handle, &major, &minor);
ROOT_DEV = MKDEV (major, minor);
if (!ROOT_DEV)
panic("I have no root and I want to scream");
n = devfs_generate_path (handle, path + 5, sizeof (path) - 5);
if (n >= 0) {
name = path + n;
devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT,
name + 5, NULL, NULL);
memcpy (name, "/dev/", 5);
}
}
retry:
bdev = bdget(kdev_t_to_nr(ROOT_DEV));
if (!bdev)
panic(__FUNCTION__ ": unable to allocate root device");
bdev->bd_op = devfs_get_ops (handle);
mode = FMODE_READ;
if (!(root_mountflags & MS_RDONLY))
mode |= FMODE_WRITE;
retval = blkdev_get(bdev, mode, 0, BDEV_FS);
if (retval == -EROFS) {
root_mountflags |= MS_RDONLY;
goto retry;
}
if (retval) {
/*
* Allow the user to distinguish between failed open
* and bad superblock on root device.
*/
Eio:
printk ("VFS: Cannot open root device \"%s\" or %s\n",
root_device_name, kdevname (ROOT_DEV));
printk ("Please append a correct \"root=\" boot option\n");
panic("VFS: Unable to mount root fs on %s",
kdevname(ROOT_DEV));
}
check_disk_change(ROOT_DEV);
sb = get_super(ROOT_DEV);
if (sb) {
/* FIXME */
p = (char *)sb->s_type->name;
atomic_inc(&sb->s_active);
up_read(&sb->s_umount);
down_write(&sb->s_umount);
goto mount_it;
}
for (p = fs_names; *p; p += strlen(p)+1) {
struct file_system_type * fs_type = get_fs_type(p);
if (!fs_type)
continue;
atomic_inc(&bdev->bd_count);
retval = blkdev_get(bdev, mode, 0, BDEV_FS);
if (retval)
goto Eio;
sb = read_super(ROOT_DEV, bdev, fs_type,
root_mountflags, root_mount_data);
put_filesystem(fs_type);
if (sb) {
blkdev_put(bdev, BDEV_FS);
goto mount_it;
}
}
panic("VFS: Unable to mount root fs on %s", kdevname(ROOT_DEV));
mount_it:
/* FIXME */
up_write(&sb->s_umount);
printk ("VFS: Mounted root (%s filesystem)%s.\n", p,
(sb->s_flags & MS_RDONLY) ? " readonly" : "");
putname(fs_names);
vfsmnt = alloc_vfsmnt();
if (!vfsmnt)
panic("VFS: alloc_vfsmnt failed for root fs");
set_devname(vfsmnt, name);
vfsmnt->mnt_sb = sb;
vfsmnt->mnt_root = dget(sb->s_root);
bdput(bdev); /* sb holds a reference */
#ifdef CONFIG_ROOT_NFS
attach_it:
#endif
root_nd.mnt = root_vfsmnt;
root_nd.dentry = root_vfsmnt->mnt_sb->s_root;
graft_tree(vfsmnt, &root_nd);
set_fs_root(current->fs, vfsmnt, vfsmnt->mnt_root);
set_fs_pwd(current->fs, vfsmnt, vfsmnt->mnt_root);
mntput(vfsmnt);
}
......@@ -100,7 +100,6 @@ struct bio {
#define bio_iovec_idx(bio, idx) (&((bio)->bi_io_vec[(idx)]))
#define bio_iovec(bio) bio_iovec_idx((bio), (bio)->bi_idx)
#define bio_page(bio) bio_iovec((bio))->bv_page
#define __bio_offset(bio, idx) bio_iovec_idx((bio), (idx))->bv_offset
#define bio_offset(bio) bio_iovec((bio))->bv_offset
#define bio_sectors(bio) ((bio)->bi_size >> 9)
#define bio_data(bio) (page_address(bio_page((bio))) + bio_offset((bio)))
......@@ -136,11 +135,18 @@ struct bio {
#define bio_io_error(bio) bio_endio((bio), 0, bio_sectors((bio)))
#define bio_for_each_segment(bvl, bio, i) \
for (bvl = bio_iovec((bio)), i = (bio)->bi_idx; \
/*
* drivers should not use the __ version unless they _really_ want to
* run through the entire bio and not just pending pieces
*/
#define __bio_for_each_segment(bvl, bio, i, start_idx) \
for (bvl = bio_iovec_idx((bio), (start_idx)), i = (start_idx); \
i < (bio)->bi_vcnt; \
bvl++, i++)
#define bio_for_each_segment(bvl, bio, i) \
__bio_for_each_segment(bvl, bio, i, (bio)->bi_idx)
/*
* get a reference to a bio, so it won't disappear. the intended use is
* something like:
......
......@@ -22,6 +22,9 @@ extern void add_blkdev_randomness(int major);
extern unsigned long initrd_start,initrd_end;
extern int mount_initrd; /* zero if initrd should not be mounted */
extern int initrd_below_start_ok; /* 1 if it is not an error if initrd_start < memory_start */
extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */
extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
extern int rd_image_start; /* starting block # of image */
void initrd_init(void);
#endif
......
......@@ -113,10 +113,6 @@ struct iobus_driver {
int (*add_device) (struct iobus*, char*);
};
extern int iobus_register_driver(struct iobus_driver * driver);
extern void iobus_unregister_driver(struct iobus_driver * driver);
extern struct iobus_driver * iobus_find_driver(char *name);
struct iobus {
spinlock_t lock; /* lock for bus */
atomic_t refcount;
......
......@@ -1447,11 +1447,9 @@ extern char root_device_name[];
extern void show_buffers(void);
extern void mount_root(void);
#ifdef CONFIG_BLK_DEV_INITRD
extern unsigned int real_root_dev;
extern int change_root(kdev_t, const char *);
#endif
extern ssize_t char_read(struct file *, char *, size_t, loff_t *);
......
......@@ -912,7 +912,7 @@
#define PCI_DEVICE_ID_TTI_HPT366 0x0004
#define PCI_VENDOR_ID_VIA 0x1106
#define PCI_DEVICE_ID_VIA_8363_0 0x0305
#define PCI_DEVICE_ID_VIA_8363_0 0x0305
#define PCI_DEVICE_ID_VIA_8371_0 0x0391
#define PCI_DEVICE_ID_VIA_8501_0 0x0501
#define PCI_DEVICE_ID_VIA_82C505 0x0505
......@@ -946,9 +946,11 @@
#define PCI_DEVICE_ID_VIA_8233_7 0x3065
#define PCI_DEVICE_ID_VIA_82C686_6 0x3068
#define PCI_DEVICE_ID_VIA_8233_0 0x3074
#define PCI_DEVICE_ID_VIA_8622 0x3102
#define PCI_DEVICE_ID_VIA_8233C_0 0x3109
#define PCI_DEVICE_ID_VIA_8361 0x3112
#define PCI_DEVICE_ID_VIA_8633_0 0x3091
#define PCI_DEVICE_ID_VIA_8367_0 0x3099
#define PCI_DEVICE_ID_VIA_8367_0 0x3099
#define PCI_DEVICE_ID_VIA_86C100A 0x6100
#define PCI_DEVICE_ID_VIA_8231 0x8231
#define PCI_DEVICE_ID_VIA_8231_4 0x8235
......
This diff is collapsed.
......@@ -120,17 +120,10 @@ extern void softirq_init(void);
int rows, cols;
#ifdef CONFIG_BLK_DEV_INITRD
unsigned int real_root_dev; /* do_proc_dointvec cannot handle kdev_t */
#endif
int root_mountflags = MS_RDONLY;
char *execute_command;
char root_device_name[64];
static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
static int __init profile_setup(char *str)
{
......@@ -141,175 +134,6 @@ static int __init profile_setup(char *str)
__setup("profile=", profile_setup);
static struct dev_name_struct {
const char *name;
const int num;
} root_dev_names[] __initdata = {
{ "nfs", 0x00ff },
{ "hda", 0x0300 },
{ "hdb", 0x0340 },
{ "loop", 0x0700 },
{ "hdc", 0x1600 },
{ "hdd", 0x1640 },
{ "hde", 0x2100 },
{ "hdf", 0x2140 },
{ "hdg", 0x2200 },
{ "hdh", 0x2240 },
{ "hdi", 0x3800 },
{ "hdj", 0x3840 },
{ "hdk", 0x3900 },
{ "hdl", 0x3940 },
{ "hdm", 0x5800 },
{ "hdn", 0x5840 },
{ "hdo", 0x5900 },
{ "hdp", 0x5940 },
{ "hdq", 0x5A00 },
{ "hdr", 0x5A40 },
{ "hds", 0x5B00 },
{ "hdt", 0x5B40 },
{ "sda", 0x0800 },
{ "sdb", 0x0810 },
{ "sdc", 0x0820 },
{ "sdd", 0x0830 },
{ "sde", 0x0840 },
{ "sdf", 0x0850 },
{ "sdg", 0x0860 },
{ "sdh", 0x0870 },
{ "sdi", 0x0880 },
{ "sdj", 0x0890 },
{ "sdk", 0x08a0 },
{ "sdl", 0x08b0 },
{ "sdm", 0x08c0 },
{ "sdn", 0x08d0 },
{ "sdo", 0x08e0 },
{ "sdp", 0x08f0 },
{ "ada", 0x1c00 },
{ "adb", 0x1c10 },
{ "adc", 0x1c20 },
{ "add", 0x1c30 },
{ "ade", 0x1c40 },
{ "fd", 0x0200 },
{ "md", 0x0900 },
{ "xda", 0x0d00 },
{ "xdb", 0x0d40 },
{ "ram", 0x0100 },
{ "scd", 0x0b00 },
{ "mcd", 0x1700 },
{ "cdu535", 0x1800 },
{ "sonycd", 0x1800 },
{ "aztcd", 0x1d00 },
{ "cm206cd", 0x2000 },
{ "gscd", 0x1000 },
{ "sbpcd", 0x1900 },
{ "eda", 0x2400 },
{ "edb", 0x2440 },
{ "pda", 0x2d00 },
{ "pdb", 0x2d10 },
{ "pdc", 0x2d20 },
{ "pdd", 0x2d30 },
{ "pcd", 0x2e00 },
{ "pf", 0x2f00 },
{ "apblock", APBLOCK_MAJOR << 8},
{ "ddv", DDV_MAJOR << 8},
{ "jsfd", JSFD_MAJOR << 8},
#if defined(CONFIG_ARCH_S390)
{ "dasda", (DASD_MAJOR << MINORBITS) },
{ "dasdb", (DASD_MAJOR << MINORBITS) + (1 << 2) },
{ "dasdc", (DASD_MAJOR << MINORBITS) + (2 << 2) },
{ "dasdd", (DASD_MAJOR << MINORBITS) + (3 << 2) },
{ "dasde", (DASD_MAJOR << MINORBITS) + (4 << 2) },
{ "dasdf", (DASD_MAJOR << MINORBITS) + (5 << 2) },
{ "dasdg", (DASD_MAJOR << MINORBITS) + (6 << 2) },
{ "dasdh", (DASD_MAJOR << MINORBITS) + (7 << 2) },
#endif
#if defined(CONFIG_BLK_CPQ_DA) || defined(CONFIG_BLK_CPQ_DA_MODULE)
{ "ida/c0d0p",0x4800 },
{ "ida/c0d1p",0x4810 },
{ "ida/c0d2p",0x4820 },
{ "ida/c0d3p",0x4830 },
{ "ida/c0d4p",0x4840 },
{ "ida/c0d5p",0x4850 },
{ "ida/c0d6p",0x4860 },
{ "ida/c0d7p",0x4870 },
{ "ida/c0d8p",0x4880 },
{ "ida/c0d9p",0x4890 },
{ "ida/c0d10p",0x48A0 },
{ "ida/c0d11p",0x48B0 },
{ "ida/c0d12p",0x48C0 },
{ "ida/c0d13p",0x48D0 },
{ "ida/c0d14p",0x48E0 },
{ "ida/c0d15p",0x48F0 },
#endif
#if defined(CONFIG_BLK_CPQ_CISS_DA) || defined(CONFIG_BLK_CPQ_CISS_DA_MODULE)
{ "cciss/c0d0p",0x6800 },
{ "cciss/c0d1p",0x6810 },
{ "cciss/c0d2p",0x6820 },
{ "cciss/c0d3p",0x6830 },
{ "cciss/c0d4p",0x6840 },
{ "cciss/c0d5p",0x6850 },
{ "cciss/c0d6p",0x6860 },
{ "cciss/c0d7p",0x6870 },
{ "cciss/c0d8p",0x6880 },
{ "cciss/c0d9p",0x6890 },
{ "cciss/c0d10p",0x68A0 },
{ "cciss/c0d11p",0x68B0 },
{ "cciss/c0d12p",0x68C0 },
{ "cciss/c0d13p",0x68D0 },
{ "cciss/c0d14p",0x68E0 },
{ "cciss/c0d15p",0x68F0 },
#endif
{ "nftla", 0x5d00 },
{ "nftlb", 0x5d10 },
{ "nftlc", 0x5d20 },
{ "nftld", 0x5d30 },
{ "ftla", 0x2c00 },
{ "ftlb", 0x2c08 },
{ "ftlc", 0x2c10 },
{ "ftld", 0x2c18 },
{ "mtdblock", 0x1f00 },
{ NULL, 0 }
};
kdev_t __init name_to_kdev_t(char *line)
{
int base = 0;
if (strncmp(line,"/dev/",5) == 0) {
struct dev_name_struct *dev = root_dev_names;
line += 5;
do {
int len = strlen(dev->name);
if (strncmp(line,dev->name,len) == 0) {
line += len;
base = dev->num;
break;
}
dev++;
} while (dev->name);
}
return to_kdev_t(base + simple_strtoul(line,NULL,base?10:16));
}
static int __init root_dev_setup(char *line)
{
int i;
char ch;
ROOT_DEV = name_to_kdev_t(line);
memset (root_device_name, 0, sizeof root_device_name);
if (strncmp (line, "/dev/", 5) == 0) line += 5;
for (i = 0; i < sizeof root_device_name - 1; ++i)
{
ch = line[i];
if ( isspace (ch) || (ch == ',') || (ch == '\0') ) break;
root_device_name[i] = ch;
}
return 1;
}
__setup("root=", root_dev_setup);
static int __init checksetup(char *line)
{
struct kernel_param *p;
......@@ -376,22 +200,6 @@ void __init calibrate_delay(void)
(loops_per_jiffy/(5000/HZ)) % 100);
}
static int __init readonly(char *str)
{
if (*str)
return 0;
root_mountflags |= MS_RDONLY;
return 1;
}
static int __init readwrite(char *str)
{
if (*str)
return 0;
root_mountflags &= ~MS_RDONLY;
return 1;
}
static int __init debug_kernel(char *str)
{
if (*str)
......@@ -408,8 +216,6 @@ static int __init quiet_kernel(char *str)
return 1;
}
__setup("ro", readonly);
__setup("rw", readwrite);
__setup("debug", debug_kernel);
__setup("quiet", quiet_kernel);
......@@ -624,21 +430,6 @@ asmlinkage void __init start_kernel(void)
rest_init();
}
#ifdef CONFIG_BLK_DEV_INITRD
static int do_linuxrc(void * shell)
{
static char *argv[] = { "linuxrc", NULL, };
close(0);close(1);close(2);
setsid();
(void) open("/dev/console",O_RDWR,0);
(void) dup(0);
(void) dup(0);
return execve(shell, argv, envp_init);
}
#endif
struct task_struct *child_reaper = &init_task;
static void __init do_initcalls(void)
......@@ -745,61 +536,7 @@ static void __init do_basic_setup(void)
#endif
}
extern void rd_load(void);
extern void initrd_load(void);
/*
* Prepare the namespace - decide what/where to mount, load ramdisks, etc.
*/
static void prepare_namespace(void)
{
#ifdef CONFIG_BLK_DEV_INITRD
int real_root_mountflags = root_mountflags;
if (!initrd_start)
mount_initrd = 0;
if (mount_initrd)
root_mountflags &= ~MS_RDONLY;
real_root_dev = ROOT_DEV;
#endif
#ifdef CONFIG_BLK_DEV_RAM
#ifdef CONFIG_BLK_DEV_INITRD
if (mount_initrd)
initrd_load();
else
#endif
rd_load();
#endif
/* Mount the root filesystem.. */
mount_root();
mount_devfs_fs ();
#ifdef CONFIG_BLK_DEV_INITRD
root_mountflags = real_root_mountflags;
if (mount_initrd && ROOT_DEV != real_root_dev
&& MAJOR(ROOT_DEV) == RAMDISK_MAJOR && MINOR(ROOT_DEV) == 0) {
int error;
int i, pid;
pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
if (pid > 0) {
while (pid != wait(&i)) {
current->policy |= SCHED_YIELD;
schedule();
}
}
if (MAJOR(real_root_dev) != RAMDISK_MAJOR
|| MINOR(real_root_dev) != 0) {
error = change_root(real_root_dev,"/initrd");
if (error)
printk(KERN_ERR "Change root to /initrd: "
"error %d\n",error);
}
}
#endif
}
extern void prepare_namespace(void);
static int init(void * unused)
{
......
......@@ -50,11 +50,6 @@ int (*platform_notify)(struct device * dev) = NULL;
int (*platform_notify_remove)(struct device * dev) = NULL;
static spinlock_t device_lock;
static LIST_HEAD(device_gc_list);
static int kdeviced_pid = 0;
static DECLARE_WAIT_QUEUE_HEAD(kdeviced_wait);
static DECLARE_COMPLETION(kdeviced_exited);
static ssize_t device_read_status(char *, size_t, loff_t, void *);
static ssize_t device_write_status(const char *, size_t, loff_t, void *);
......@@ -379,12 +374,24 @@ void put_device(struct device * dev)
list_del_init(&dev->node);
unlock_iobus(parent);
/* queue the device to be removed by the reaper. */
spin_lock(&device_lock);
list_add_tail(&dev->node,&device_gc_list);
spin_unlock(&device_lock);
/* remove the driverfs directory */
device_remove_dir(dev);
if (dev->subordinate)
iobus_remove_dir(dev->subordinate);
wake_up(&kdeviced_wait);
/* Notify the platform of the removal, in case they
* need to do anything...
*/
if (platform_notify_remove)
platform_notify_remove(dev);
/* Tell the driver to clean up after itself.
* Note that we likely didn't allocate the device,
* so this is the driver's chance to free that up...
*/
if (dev->driver && dev->driver->remove)
dev->driver->remove(dev,REMOVE_FREE_RESOURCES);
put_iobus(parent);
}
......@@ -875,76 +882,6 @@ static ssize_t iobus_write_status(const char *buf, size_t count, loff_t off, voi
return error < 0 ? error : count;
}
/* Device Garbage Collection
* When a device's reference count reaches 0, it is removed from it's
* parent's list and added to a list of devices waiting to be removed.
*
* We don't directly remove it ourselves, because someone could have an
* open file.
*
* We don't allocate an event for keventd, becuase we may be here from
* an interrupt; and how do those things get freed, anyway?
*
* Instead, when a device's reference count reaches 0, it is removed
* from its parent's list of children and added to the list of devices
* to be reaped.
*
* When we spawn a thread that gets woken up every time a device is added
* to the unused list.
*/
static inline void __reap_device(struct device * dev)
{
/* FIXME: What do we do for a bridge? */
/* remove the driverfs directory */
device_remove_dir(dev);
if (dev->subordinate)
iobus_remove_dir(dev->subordinate);
/* Notify the platform of the removal, in case they
* need to do anything...
*/
if (platform_notify_remove)
platform_notify_remove(dev);
/* Tell the driver to clean up after itself.
* Note that we likely didn't allocate the device,
* so this is the driver's chance to free that up...
*/
if (dev->driver && dev->driver->remove)
dev->driver->remove(dev,REMOVE_FREE_RESOURCES);
}
static int device_cleanup_thread(void * data)
{
daemonize();
strcpy(current->comm,"kdeviced");
do {
struct list_head * node;
spin_lock(&device_lock);
node = device_gc_list.next;
while(node != &device_gc_list) {
list_del_init(node);
spin_unlock(&device_lock);
__reap_device(list_to_dev(node));
spin_lock(&device_lock);
node = device_gc_list.next;
}
spin_unlock(&device_lock);
interruptible_sleep_on(&kdeviced_wait);
} while(!signal_pending(current));
DBG("kdeviced exiting\n");
complete_and_exit(&kdeviced_exited,0);
return 0;
}
static int __init device_init_root(void)
{
/* initialize parent bus lists */
......@@ -981,27 +918,13 @@ int __init device_driver_init(void)
return error;
}
/* initialise the garbage collection */
pid = kernel_thread(device_cleanup_thread,NULL,
(CLONE_FS | CLONE_FILES | CLONE_SIGHAND));
if (pid > 0)
kdeviced_pid = pid;
else {
DBG("DEV: Could not start cleanup thread\n");
return pid;
}
DBG("DEV: Done Initialising\n");
return error;
}
void __exit device_driver_exit(void)
{
if (kdeviced_pid) {
kill_proc(kdeviced_pid,SIGTERM,1);
kdeviced_pid = 0;
wait_for_completion(&kdeviced_exited);
}
}
static int __init device_setup(char *str)
......
......@@ -214,7 +214,7 @@ static inline void copy_to_high_bio_irq(struct bio *to, struct bio *from)
struct bio_vec *tovec, *fromvec;
int i;
bio_for_each_segment(tovec, to, i) {
__bio_for_each_segment(tovec, to, i, 0) {
fromvec = &from->bi_io_vec[i];
/*
......@@ -225,12 +225,11 @@ static inline void copy_to_high_bio_irq(struct bio *to, struct bio *from)
vfrom = page_address(fromvec->bv_page) + fromvec->bv_offset;
__save_flags(flags);
__cli();
local_irq_save(flags);
vto = kmap_atomic(tovec->bv_page, KM_BOUNCE_READ);
memcpy(vto + tovec->bv_offset, vfrom, to->bi_size);
memcpy(vto + tovec->bv_offset, vfrom, tovec->bv_len);
kunmap_atomic(vto, KM_BOUNCE_READ);
__restore_flags(flags);
local_irq_restore(flags);
}
}
......@@ -263,28 +262,39 @@ __initcall(init_emergency_pool);
static inline int bounce_end_io (struct bio *bio, int nr_sectors)
{
struct bio *bio_orig = bio->bi_private;
struct page *page = bio_page(bio);
struct bio_vec *bvec, *org_vec;
unsigned long flags;
int ret;
int ret, i;
if (test_bit(BIO_UPTODATE, &bio->bi_flags))
set_bit(BIO_UPTODATE, &bio_orig->bi_flags);
if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
goto out_eio;
ret = bio_orig->bi_end_io(bio_orig, nr_sectors);
set_bit(BIO_UPTODATE, &bio_orig->bi_flags);
/*
* free up bounce indirect pages used
*/
spin_lock_irqsave(&emergency_lock, flags);
if (nr_emergency_pages >= POOL_SIZE) {
spin_unlock_irqrestore(&emergency_lock, flags);
__free_page(page);
} else {
/*
* We are abusing page->list to manage
* the highmem emergency pool:
*/
list_add(&page->list, &emergency_pages);
nr_emergency_pages++;
spin_unlock_irqrestore(&emergency_lock, flags);
__bio_for_each_segment(bvec, bio, i, 0) {
org_vec = &bio_orig->bi_io_vec[i];
if (bvec->bv_page == org_vec->bv_page)
continue;
if (nr_emergency_pages >= POOL_SIZE)
__free_page(bvec->bv_page);
else {
/*
* We are abusing page->list to manage
* the highmem emergency pool:
*/
list_add(&bvec->bv_page->list, &emergency_pages);
nr_emergency_pages++;
}
}
spin_unlock_irqrestore(&emergency_lock, flags);
out_eio:
ret = bio_orig->bi_end_io(bio_orig, nr_sectors);
bio_put(bio);
return ret;
......
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