Skip to content

Commit

Permalink
Merge git://git.kernel.org/pub/scm/linux/kernel/git/arjan/linux-2.6-a…
Browse files Browse the repository at this point in the history
…sync

* git://git.kernel.org/pub/scm/linux/kernel/git/arjan/linux-2.6-async:
  async: don't do the initcall stuff post boot
  bootchart: improve output based on Dave Jones' feedback
  async: make the final inode deletion an asynchronous event
  fastboot: Make libata initialization even more async
  fastboot: make the libata port scan asynchronous
  fastboot: make scsi probes asynchronous
  async: Asynchronous function calls to speed up kernel boot
  • Loading branch information
torvalds committed Jan 7, 2009
2 parents b13d372 + ad160d2 commit 67acd8b
Show file tree
Hide file tree
Showing 13 changed files with 510 additions and 96 deletions.
96 changes: 51 additions & 45 deletions drivers/ata/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include <linux/workqueue.h>
#include <linux/scatterlist.h>
#include <linux/io.h>
#include <linux/async.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_host.h>
Expand Down Expand Up @@ -5909,6 +5910,54 @@ void ata_host_init(struct ata_host *host, struct device *dev,
host->ops = ops;
}


static void async_port_probe(void *data, async_cookie_t cookie)
{
int rc;
struct ata_port *ap = data;
/* probe */
if (ap->ops->error_handler) {
struct ata_eh_info *ehi = &ap->link.eh_info;
unsigned long flags;

ata_port_probe(ap);

/* kick EH for boot probing */
spin_lock_irqsave(ap->lock, flags);

ehi->probe_mask |= ATA_ALL_DEVICES;
ehi->action |= ATA_EH_RESET | ATA_EH_LPM;
ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET;

ap->pflags &= ~ATA_PFLAG_INITIALIZING;
ap->pflags |= ATA_PFLAG_LOADING;
ata_port_schedule_eh(ap);

spin_unlock_irqrestore(ap->lock, flags);

/* wait for EH to finish */
ata_port_wait_eh(ap);
} else {
DPRINTK("ata%u: bus probe begin\n", ap->print_id);
rc = ata_bus_probe(ap);
DPRINTK("ata%u: bus probe end\n", ap->print_id);

if (rc) {
/* FIXME: do something useful here?
* Current libata behavior will
* tear down everything when
* the module is removed
* or the h/w is unplugged.
*/
}
}

/* in order to keep device order, we need to synchronize at this point */
async_synchronize_cookie(cookie);

ata_scsi_scan_host(ap, 1);

}
/**
* ata_host_register - register initialized ATA host
* @host: ATA host to register
Expand Down Expand Up @@ -5988,52 +6037,9 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
DPRINTK("probe begin\n");
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];

/* probe */
if (ap->ops->error_handler) {
struct ata_eh_info *ehi = &ap->link.eh_info;
unsigned long flags;

ata_port_probe(ap);

/* kick EH for boot probing */
spin_lock_irqsave(ap->lock, flags);

ehi->probe_mask |= ATA_ALL_DEVICES;
ehi->action |= ATA_EH_RESET | ATA_EH_LPM;
ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET;

ap->pflags &= ~ATA_PFLAG_INITIALIZING;
ap->pflags |= ATA_PFLAG_LOADING;
ata_port_schedule_eh(ap);

spin_unlock_irqrestore(ap->lock, flags);

/* wait for EH to finish */
ata_port_wait_eh(ap);
} else {
DPRINTK("ata%u: bus probe begin\n", ap->print_id);
rc = ata_bus_probe(ap);
DPRINTK("ata%u: bus probe end\n", ap->print_id);

if (rc) {
/* FIXME: do something useful here?
* Current libata behavior will
* tear down everything when
* the module is removed
* or the h/w is unplugged.
*/
}
}
}

/* probes are done, now scan each port's disk(s) */
DPRINTK("host probe begin\n");
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];

ata_scsi_scan_host(ap, 1);
async_schedule(async_port_probe, ap);
}
DPRINTK("probe end\n");

return 0;
}
Expand Down
3 changes: 3 additions & 0 deletions drivers/scsi/scsi_scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/spinlock.h>
#include <linux/async.h>

#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
Expand Down Expand Up @@ -179,6 +180,8 @@ int scsi_complete_async_scans(void)
spin_unlock(&async_scan_lock);

kfree(data);
/* Synchronize async operations globally */
async_synchronize_full();
return 0;
}

Expand Down
109 changes: 67 additions & 42 deletions drivers/scsi/sd.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/string_helpers.h>
#include <linux/async.h>
#include <asm/uaccess.h>

#include <scsi/scsi.h>
Expand Down Expand Up @@ -1802,6 +1803,71 @@ static int sd_format_disk_name(char *prefix, int index, char *buf, int buflen)
return 0;
}

/*
* The asynchronous part of sd_probe
*/
static void sd_probe_async(void *data, async_cookie_t cookie)
{
struct scsi_disk *sdkp = data;
struct scsi_device *sdp;
struct gendisk *gd;
u32 index;
struct device *dev;

sdp = sdkp->device;
gd = sdkp->disk;
index = sdkp->index;
dev = &sdp->sdev_gendev;

if (!sdp->request_queue->rq_timeout) {
if (sdp->type != TYPE_MOD)
blk_queue_rq_timeout(sdp->request_queue, SD_TIMEOUT);
else
blk_queue_rq_timeout(sdp->request_queue,
SD_MOD_TIMEOUT);
}

device_initialize(&sdkp->dev);
sdkp->dev.parent = &sdp->sdev_gendev;
sdkp->dev.class = &sd_disk_class;
strncpy(sdkp->dev.bus_id, sdp->sdev_gendev.bus_id, BUS_ID_SIZE);

if (device_add(&sdkp->dev))
goto out_free_index;

get_device(&sdp->sdev_gendev);

if (index < SD_MAX_DISKS) {
gd->major = sd_major((index & 0xf0) >> 4);
gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
gd->minors = SD_MINORS;
}
gd->fops = &sd_fops;
gd->private_data = &sdkp->driver;
gd->queue = sdkp->device->request_queue;

sd_revalidate_disk(gd);

blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);

gd->driverfs_dev = &sdp->sdev_gendev;
gd->flags = GENHD_FL_EXT_DEVT | GENHD_FL_DRIVERFS;
if (sdp->removable)
gd->flags |= GENHD_FL_REMOVABLE;

dev_set_drvdata(dev, sdkp);
add_disk(gd);
sd_dif_config_host(sdkp);

sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
sdp->removable ? "removable " : "");

return;

out_free_index:
ida_remove(&sd_index_ida, index);
}

/**
* sd_probe - called during driver initialization and whenever a
* new scsi device is attached to the system. It is called once
Expand Down Expand Up @@ -1865,48 +1931,7 @@ static int sd_probe(struct device *dev)
sdkp->openers = 0;
sdkp->previous_state = 1;

if (!sdp->request_queue->rq_timeout) {
if (sdp->type != TYPE_MOD)
blk_queue_rq_timeout(sdp->request_queue, SD_TIMEOUT);
else
blk_queue_rq_timeout(sdp->request_queue,
SD_MOD_TIMEOUT);
}

device_initialize(&sdkp->dev);
sdkp->dev.parent = &sdp->sdev_gendev;
sdkp->dev.class = &sd_disk_class;
strncpy(sdkp->dev.bus_id, sdp->sdev_gendev.bus_id, BUS_ID_SIZE);

if (device_add(&sdkp->dev))
goto out_free_index;

get_device(&sdp->sdev_gendev);

if (index < SD_MAX_DISKS) {
gd->major = sd_major((index & 0xf0) >> 4);
gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
gd->minors = SD_MINORS;
}
gd->fops = &sd_fops;
gd->private_data = &sdkp->driver;
gd->queue = sdkp->device->request_queue;

sd_revalidate_disk(gd);

blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);

gd->driverfs_dev = &sdp->sdev_gendev;
gd->flags = GENHD_FL_EXT_DEVT | GENHD_FL_DRIVERFS;
if (sdp->removable)
gd->flags |= GENHD_FL_REMOVABLE;

dev_set_drvdata(dev, sdkp);
add_disk(gd);
sd_dif_config_host(sdkp);

sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
sdp->removable ? "removable " : "");
async_schedule(sd_probe_async, sdkp);

return 0;

Expand Down
20 changes: 13 additions & 7 deletions fs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <linux/bootmem.h>
#include <linux/inotify.h>
#include <linux/mount.h>
#include <linux/async.h>

/*
* This is needed for the following functions:
Expand Down Expand Up @@ -1138,16 +1139,11 @@ EXPORT_SYMBOL(remove_inode_hash);
* I_FREEING is set so that no-one will take a new reference to the inode while
* it is being deleted.
*/
void generic_delete_inode(struct inode *inode)
static void generic_delete_inode_async(void *data, async_cookie_t cookie)
{
struct inode *inode = data;
const struct super_operations *op = inode->i_sb->s_op;

list_del_init(&inode->i_list);
list_del_init(&inode->i_sb_list);
inode->i_state |= I_FREEING;
inodes_stat.nr_inodes--;
spin_unlock(&inode_lock);

security_inode_delete(inode);

if (op->delete_inode) {
Expand All @@ -1171,6 +1167,16 @@ void generic_delete_inode(struct inode *inode)
destroy_inode(inode);
}

void generic_delete_inode(struct inode *inode)
{
list_del_init(&inode->i_list);
list_del_init(&inode->i_sb_list);
inode->i_state |= I_FREEING;
inodes_stat.nr_inodes--;
spin_unlock(&inode_lock);
async_schedule_special(generic_delete_inode_async, inode, &inode->i_sb->s_async_list);
}

EXPORT_SYMBOL(generic_delete_inode);

static void generic_forget_inode(struct inode *inode)
Expand Down
10 changes: 10 additions & 0 deletions fs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <linux/kobject.h>
#include <linux/mutex.h>
#include <linux/file.h>
#include <linux/async.h>
#include <asm/uaccess.h>
#include "internal.h"

Expand Down Expand Up @@ -71,6 +72,7 @@ static struct super_block *alloc_super(struct file_system_type *type)
INIT_HLIST_HEAD(&s->s_anon);
INIT_LIST_HEAD(&s->s_inodes);
INIT_LIST_HEAD(&s->s_dentry_lru);
INIT_LIST_HEAD(&s->s_async_list);
init_rwsem(&s->s_umount);
mutex_init(&s->s_lock);
lockdep_set_class(&s->s_umount, &type->s_umount_key);
Expand Down Expand Up @@ -289,11 +291,18 @@ void generic_shutdown_super(struct super_block *sb)
{
const struct super_operations *sop = sb->s_op;


if (sb->s_root) {
shrink_dcache_for_umount(sb);
fsync_super(sb);
lock_super(sb);
sb->s_flags &= ~MS_ACTIVE;

/*
* wait for asynchronous fs operations to finish before going further
*/
async_synchronize_full_special(&sb->s_async_list);

/* bad name - it should be evict_inodes() */
invalidate_inodes(sb);
lock_kernel();
Expand Down Expand Up @@ -449,6 +458,7 @@ void sync_filesystems(int wait)
if (sb->s_flags & MS_RDONLY)
continue;
sb->s_need_sync_fs = 1;
async_synchronize_full_special(&sb->s_async_list);
}

restart:
Expand Down
25 changes: 25 additions & 0 deletions include/linux/async.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* async.h: Asynchronous function calls for boot performance
*
* (C) Copyright 2009 Intel Corporation
* Author: Arjan van de Ven <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*/

#include <linux/types.h>
#include <linux/list.h>

typedef u64 async_cookie_t;
typedef void (async_func_ptr) (void *data, async_cookie_t cookie);

extern async_cookie_t async_schedule(async_func_ptr *ptr, void *data);
extern async_cookie_t async_schedule_special(async_func_ptr *ptr, void *data, struct list_head *list);
extern void async_synchronize_full(void);
extern void async_synchronize_full_special(struct list_head *list);
extern void async_synchronize_cookie(async_cookie_t cookie);
extern void async_synchronize_cookie_special(async_cookie_t cookie, struct list_head *list);

5 changes: 5 additions & 0 deletions include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1184,6 +1184,11 @@ struct super_block {
* generic_show_options()
*/
char *s_options;

/*
* storage for asynchronous operations
*/
struct list_head s_async_list;
};

extern struct timespec current_fs_time(struct super_block *sb);
Expand Down
Loading

0 comments on commit 67acd8b

Please sign in to comment.