Commit c53dbf54 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.dk/linux-2.6-block

* 'for-linus' of git://git.kernel.dk/linux-2.6-block:
  block: remove __generic_unplug_device() from exports
  block: move q->unplug_work initialization
  blktrace: pass zfcp driver data
  blktrace: add support for driver data
  block: fix current kernel-doc warnings
  block: only call ->request_fn when the queue is not stopped
  block: simplify string handling in elv_iosched_store()
  block: fix kernel-doc for blk_alloc_devt()
  block: fix nr_phys_segments miscalculation bug
  block: add partition attribute for partition number
  block: add BIG FAT WARNING to CONFIG_DEBUG_BLOCK_EXT_DEVT
  softirq: Add support for triggering softirq work on softirqs.
parents b73b636e f73e2d13
...@@ -257,7 +257,6 @@ void __generic_unplug_device(struct request_queue *q) ...@@ -257,7 +257,6 @@ void __generic_unplug_device(struct request_queue *q)
q->request_fn(q); q->request_fn(q);
} }
EXPORT_SYMBOL(__generic_unplug_device);
/** /**
* generic_unplug_device - fire a request queue * generic_unplug_device - fire a request queue
...@@ -325,6 +324,9 @@ EXPORT_SYMBOL(blk_unplug); ...@@ -325,6 +324,9 @@ EXPORT_SYMBOL(blk_unplug);
static void blk_invoke_request_fn(struct request_queue *q) static void blk_invoke_request_fn(struct request_queue *q)
{ {
if (unlikely(blk_queue_stopped(q)))
return;
/* /*
* one level of recursion is ok and is much faster than kicking * one level of recursion is ok and is much faster than kicking
* the unplug handling * the unplug handling
...@@ -399,8 +401,13 @@ void blk_sync_queue(struct request_queue *q) ...@@ -399,8 +401,13 @@ void blk_sync_queue(struct request_queue *q)
EXPORT_SYMBOL(blk_sync_queue); EXPORT_SYMBOL(blk_sync_queue);
/** /**
* blk_run_queue - run a single device queue * __blk_run_queue - run a single device queue
* @q: The queue to run * @q: The queue to run
*
* Description:
* See @blk_run_queue. This variant must be called with the queue lock
* held and interrupts disabled.
*
*/ */
void __blk_run_queue(struct request_queue *q) void __blk_run_queue(struct request_queue *q)
{ {
...@@ -418,6 +425,12 @@ EXPORT_SYMBOL(__blk_run_queue); ...@@ -418,6 +425,12 @@ EXPORT_SYMBOL(__blk_run_queue);
/** /**
* blk_run_queue - run a single device queue * blk_run_queue - run a single device queue
* @q: The queue to run * @q: The queue to run
*
* Description:
* Invoke request handling on this queue, if it has pending work to do.
* May be used to restart queueing when a request has completed. Also
* See @blk_start_queueing.
*
*/ */
void blk_run_queue(struct request_queue *q) void blk_run_queue(struct request_queue *q)
{ {
...@@ -501,6 +514,7 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) ...@@ -501,6 +514,7 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
init_timer(&q->unplug_timer); init_timer(&q->unplug_timer);
setup_timer(&q->timeout, blk_rq_timed_out_timer, (unsigned long) q); setup_timer(&q->timeout, blk_rq_timed_out_timer, (unsigned long) q);
INIT_LIST_HEAD(&q->timeout_list); INIT_LIST_HEAD(&q->timeout_list);
INIT_WORK(&q->unplug_work, blk_unplug_work);
kobject_init(&q->kobj, &blk_queue_ktype); kobject_init(&q->kobj, &blk_queue_ktype);
...@@ -884,7 +898,8 @@ EXPORT_SYMBOL(blk_get_request); ...@@ -884,7 +898,8 @@ EXPORT_SYMBOL(blk_get_request);
* *
* This is basically a helper to remove the need to know whether a queue * This is basically a helper to remove the need to know whether a queue
* is plugged or not if someone just wants to initiate dispatch of requests * is plugged or not if someone just wants to initiate dispatch of requests
* for this queue. * for this queue. Should be used to start queueing on a device outside
* of ->request_fn() context. Also see @blk_run_queue.
* *
* The queue lock must be held with interrupts disabled. * The queue lock must be held with interrupts disabled.
*/ */
...@@ -1003,8 +1018,9 @@ static void part_round_stats_single(int cpu, struct hd_struct *part, ...@@ -1003,8 +1018,9 @@ static void part_round_stats_single(int cpu, struct hd_struct *part,
} }
/** /**
* part_round_stats() - Round off the performance stats on a struct * part_round_stats() - Round off the performance stats on a struct disk_stats.
* disk_stats. * @cpu: cpu number for stats access
* @part: target partition
* *
* The average IO queue length and utilisation statistics are maintained * The average IO queue length and utilisation statistics are maintained
* by observing the current state of the queue length and the amount of * by observing the current state of the queue length and the amount of
......
...@@ -77,12 +77,20 @@ void blk_recalc_rq_segments(struct request *rq) ...@@ -77,12 +77,20 @@ void blk_recalc_rq_segments(struct request *rq)
continue; continue;
} }
new_segment: new_segment:
if (nr_phys_segs == 1 && seg_size > rq->bio->bi_seg_front_size)
rq->bio->bi_seg_front_size = seg_size;
nr_phys_segs++; nr_phys_segs++;
bvprv = bv; bvprv = bv;
seg_size = bv->bv_len; seg_size = bv->bv_len;
highprv = high; highprv = high;
} }
if (nr_phys_segs == 1 && seg_size > rq->bio->bi_seg_front_size)
rq->bio->bi_seg_front_size = seg_size;
if (seg_size > rq->biotail->bi_seg_back_size)
rq->biotail->bi_seg_back_size = seg_size;
rq->nr_phys_segments = nr_phys_segs; rq->nr_phys_segments = nr_phys_segs;
} }
...@@ -106,7 +114,8 @@ static int blk_phys_contig_segment(struct request_queue *q, struct bio *bio, ...@@ -106,7 +114,8 @@ static int blk_phys_contig_segment(struct request_queue *q, struct bio *bio,
if (!test_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags)) if (!test_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags))
return 0; return 0;
if (bio->bi_size + nxt->bi_size > q->max_segment_size) if (bio->bi_seg_back_size + nxt->bi_seg_front_size >
q->max_segment_size)
return 0; return 0;
if (!bio_has_data(bio)) if (!bio_has_data(bio))
...@@ -309,6 +318,8 @@ static int ll_merge_requests_fn(struct request_queue *q, struct request *req, ...@@ -309,6 +318,8 @@ static int ll_merge_requests_fn(struct request_queue *q, struct request *req,
struct request *next) struct request *next)
{ {
int total_phys_segments; int total_phys_segments;
unsigned int seg_size =
req->biotail->bi_seg_back_size + next->bio->bi_seg_front_size;
/* /*
* First check if the either of the requests are re-queued * First check if the either of the requests are re-queued
...@@ -324,8 +335,13 @@ static int ll_merge_requests_fn(struct request_queue *q, struct request *req, ...@@ -324,8 +335,13 @@ static int ll_merge_requests_fn(struct request_queue *q, struct request *req,
return 0; return 0;
total_phys_segments = req->nr_phys_segments + next->nr_phys_segments; total_phys_segments = req->nr_phys_segments + next->nr_phys_segments;
if (blk_phys_contig_segment(q, req->biotail, next->bio)) if (blk_phys_contig_segment(q, req->biotail, next->bio)) {
if (req->nr_phys_segments == 1)
req->bio->bi_seg_front_size = seg_size;
if (next->nr_phys_segments == 1)
next->biotail->bi_seg_back_size = seg_size;
total_phys_segments--; total_phys_segments--;
}
if (total_phys_segments > q->max_phys_segments) if (total_phys_segments > q->max_phys_segments)
return 0; return 0;
......
...@@ -141,8 +141,6 @@ void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn) ...@@ -141,8 +141,6 @@ void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)
if (q->unplug_delay == 0) if (q->unplug_delay == 0)
q->unplug_delay = 1; q->unplug_delay = 1;
INIT_WORK(&q->unplug_work, blk_unplug_work);
q->unplug_timer.function = blk_unplug_timeout; q->unplug_timer.function = blk_unplug_timeout;
q->unplug_timer.data = (unsigned long)q; q->unplug_timer.data = (unsigned long)q;
......
...@@ -20,6 +20,7 @@ void blk_unplug_timeout(unsigned long data); ...@@ -20,6 +20,7 @@ void blk_unplug_timeout(unsigned long data);
void blk_rq_timed_out_timer(unsigned long data); void blk_rq_timed_out_timer(unsigned long data);
void blk_delete_timer(struct request *); void blk_delete_timer(struct request *);
void blk_add_timer(struct request *); void blk_add_timer(struct request *);
void __generic_unplug_device(struct request_queue *);
/* /*
* Internal atomic flags for request handling * Internal atomic flags for request handling
......
...@@ -612,7 +612,7 @@ void elv_insert(struct request_queue *q, struct request *rq, int where) ...@@ -612,7 +612,7 @@ void elv_insert(struct request_queue *q, struct request *rq, int where)
* processing. * processing.
*/ */
blk_remove_plug(q); blk_remove_plug(q);
q->request_fn(q); blk_start_queueing(q);
break; break;
case ELEVATOR_INSERT_SORT: case ELEVATOR_INSERT_SORT:
...@@ -950,7 +950,7 @@ void elv_completed_request(struct request_queue *q, struct request *rq) ...@@ -950,7 +950,7 @@ void elv_completed_request(struct request_queue *q, struct request *rq)
blk_ordered_cur_seq(q) == QUEUE_ORDSEQ_DRAIN && blk_ordered_cur_seq(q) == QUEUE_ORDSEQ_DRAIN &&
blk_ordered_req_seq(first_rq) > QUEUE_ORDSEQ_DRAIN) { blk_ordered_req_seq(first_rq) > QUEUE_ORDSEQ_DRAIN) {
blk_ordered_complete_seq(q, QUEUE_ORDSEQ_DRAIN, 0); blk_ordered_complete_seq(q, QUEUE_ORDSEQ_DRAIN, 0);
q->request_fn(q); blk_start_queueing(q);
} }
} }
} }
...@@ -1109,8 +1109,7 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e) ...@@ -1109,8 +1109,7 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e)
elv_drain_elevator(q); elv_drain_elevator(q);
while (q->rq.elvpriv) { while (q->rq.elvpriv) {
blk_remove_plug(q); blk_start_queueing(q);
q->request_fn(q);
spin_unlock_irq(q->queue_lock); spin_unlock_irq(q->queue_lock);
msleep(10); msleep(10);
spin_lock_irq(q->queue_lock); spin_lock_irq(q->queue_lock);
...@@ -1166,15 +1165,10 @@ ssize_t elv_iosched_store(struct request_queue *q, const char *name, ...@@ -1166,15 +1165,10 @@ ssize_t elv_iosched_store(struct request_queue *q, const char *name,
size_t count) size_t count)
{ {
char elevator_name[ELV_NAME_MAX]; char elevator_name[ELV_NAME_MAX];
size_t len;
struct elevator_type *e; struct elevator_type *e;
elevator_name[sizeof(elevator_name) - 1] = '\0'; strlcpy(elevator_name, name, sizeof(elevator_name));
strncpy(elevator_name, name, sizeof(elevator_name) - 1); strstrip(elevator_name);
len = strlen(elevator_name);
if (len && elevator_name[len - 1] == '\n')
elevator_name[len - 1] = '\0';
e = elevator_get(elevator_name); e = elevator_get(elevator_name);
if (!e) { if (!e) {
......
...@@ -358,7 +358,6 @@ static int blk_mangle_minor(int minor) ...@@ -358,7 +358,6 @@ static int blk_mangle_minor(int minor)
/** /**
* blk_alloc_devt - allocate a dev_t for a partition * blk_alloc_devt - allocate a dev_t for a partition
* @part: partition to allocate dev_t for * @part: partition to allocate dev_t for
* @gfp_mask: memory allocation flag
* @devt: out parameter for resulting dev_t * @devt: out parameter for resulting dev_t
* *
* Allocate a dev_t for block device. * Allocate a dev_t for block device.
...@@ -535,7 +534,7 @@ void unlink_gendisk(struct gendisk *disk) ...@@ -535,7 +534,7 @@ void unlink_gendisk(struct gendisk *disk)
/** /**
* get_gendisk - get partitioning information for a given device * get_gendisk - get partitioning information for a given device
* @devt: device to get partitioning information for * @devt: device to get partitioning information for
* @part: returned partition index * @partno: returned partition index
* *
* This function gets the structure containing partitioning * This function gets the structure containing partitioning
* information for the given device @devt. * information for the given device @devt.
......
...@@ -1493,8 +1493,8 @@ void ide_do_drive_cmd(ide_drive_t *drive, struct request *rq) ...@@ -1493,8 +1493,8 @@ void ide_do_drive_cmd(ide_drive_t *drive, struct request *rq)
spin_lock_irqsave(&ide_lock, flags); spin_lock_irqsave(&ide_lock, flags);
hwgroup->rq = NULL; hwgroup->rq = NULL;
__elv_add_request(drive->queue, rq, ELEVATOR_INSERT_FRONT, 1); __elv_add_request(drive->queue, rq, ELEVATOR_INSERT_FRONT, 0);
__generic_unplug_device(drive->queue); blk_start_queueing(drive->queue);
spin_unlock_irqrestore(&ide_lock, flags); spin_unlock_irqrestore(&ide_lock, flags);
} }
......
...@@ -583,6 +583,8 @@ struct zfcp_fsf_req { ...@@ -583,6 +583,8 @@ struct zfcp_fsf_req {
unsigned long long issued; /* request sent time (STCK) */ unsigned long long issued; /* request sent time (STCK) */
struct zfcp_unit *unit; struct zfcp_unit *unit;
void (*handler)(struct zfcp_fsf_req *); void (*handler)(struct zfcp_fsf_req *);
u16 qdio_outb_usage;/* usage of outbound queue */
u16 qdio_inb_usage; /* usage of inbound queue */
}; };
/* driver data */ /* driver data */
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
* Copyright IBM Corporation 2002, 2008 * Copyright IBM Corporation 2002, 2008
*/ */
#include <linux/blktrace_api.h>
#include "zfcp_ext.h" #include "zfcp_ext.h"
static void zfcp_fsf_request_timeout_handler(unsigned long data) static void zfcp_fsf_request_timeout_handler(unsigned long data)
...@@ -777,6 +778,7 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *req) ...@@ -777,6 +778,7 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *req)
list_add_tail(&req->list, &adapter->req_list[idx]); list_add_tail(&req->list, &adapter->req_list[idx]);
spin_unlock(&adapter->req_list_lock); spin_unlock(&adapter->req_list_lock);
req->qdio_outb_usage = atomic_read(&req_q->count);
req->issued = get_clock(); req->issued = get_clock();
if (zfcp_qdio_send(req)) { if (zfcp_qdio_send(req)) {
/* Queues are down..... */ /* Queues are down..... */
...@@ -2082,6 +2084,36 @@ static void zfcp_fsf_req_latency(struct zfcp_fsf_req *req) ...@@ -2082,6 +2084,36 @@ static void zfcp_fsf_req_latency(struct zfcp_fsf_req *req)
spin_unlock_irqrestore(&unit->latencies.lock, flags); spin_unlock_irqrestore(&unit->latencies.lock, flags);
} }
#ifdef CONFIG_BLK_DEV_IO_TRACE
static void zfcp_fsf_trace_latency(struct zfcp_fsf_req *fsf_req)
{
struct fsf_qual_latency_info *lat_inf;
struct scsi_cmnd *scsi_cmnd = (struct scsi_cmnd *)fsf_req->data;
struct request *req = scsi_cmnd->request;
struct zfcp_blk_drv_data trace;
int ticks = fsf_req->adapter->timer_ticks;
trace.flags = 0;
trace.magic = ZFCP_BLK_DRV_DATA_MAGIC;
if (fsf_req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA) {
trace.flags |= ZFCP_BLK_LAT_VALID;
lat_inf = &fsf_req->qtcb->prefix.prot_status_qual.latency_info;
trace.channel_lat = lat_inf->channel_lat * ticks;
trace.fabric_lat = lat_inf->fabric_lat * ticks;
}
if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
trace.flags |= ZFCP_BLK_REQ_ERROR;
trace.inb_usage = fsf_req->qdio_inb_usage;
trace.outb_usage = fsf_req->qdio_outb_usage;
blk_add_driver_data(req->q, req, &trace, sizeof(trace));
}
#else
static inline void zfcp_fsf_trace_latency(struct zfcp_fsf_req *fsf_req)
{
}
#endif
static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req) static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req)
{ {
struct scsi_cmnd *scpnt = req->data; struct scsi_cmnd *scpnt = req->data;
...@@ -2114,6 +2146,8 @@ static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req) ...@@ -2114,6 +2146,8 @@ static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req)
if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA) if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA)
zfcp_fsf_req_latency(req); zfcp_fsf_req_latency(req);
zfcp_fsf_trace_latency(req);
if (unlikely(fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)) { if (unlikely(fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)) {
if (fcp_rsp_info[3] == RSP_CODE_GOOD) if (fcp_rsp_info[3] == RSP_CODE_GOOD)
set_host_byte(scpnt, DID_OK); set_host_byte(scpnt, DID_OK);
......
...@@ -439,4 +439,16 @@ struct fsf_qtcb { ...@@ -439,4 +439,16 @@ struct fsf_qtcb {
u8 log[FSF_QTCB_LOG_SIZE]; u8 log[FSF_QTCB_LOG_SIZE];
} __attribute__ ((packed)); } __attribute__ ((packed));
struct zfcp_blk_drv_data {
#define ZFCP_BLK_DRV_DATA_MAGIC 0x1
u32 magic;
#define ZFCP_BLK_LAT_VALID 0x1
#define ZFCP_BLK_REQ_ERROR 0x2
u16 flags;
u8 inb_usage;
u8 outb_usage;
u64 channel_lat;
u64 fabric_lat;
} __attribute__ ((packed));
#endif /* FSF_H */ #endif /* FSF_H */
...@@ -115,6 +115,7 @@ static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, ...@@ -115,6 +115,7 @@ static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter,
spin_unlock_irqrestore(&adapter->req_list_lock, flags); spin_unlock_irqrestore(&adapter->req_list_lock, flags);
fsf_req->sbal_response = sbal_idx; fsf_req->sbal_response = sbal_idx;
fsf_req->qdio_inb_usage = atomic_read(&adapter->resp_q.count);
zfcp_fsf_req_complete(fsf_req); zfcp_fsf_req_complete(fsf_req);
} }
......
...@@ -1262,7 +1262,7 @@ EXPORT_SYMBOL(ioctl_by_bdev); ...@@ -1262,7 +1262,7 @@ EXPORT_SYMBOL(ioctl_by_bdev);
/** /**
* lookup_bdev - lookup a struct block_device by name * lookup_bdev - lookup a struct block_device by name
* @pathname: special file representing the block device * @path: special file representing the block device
* *
* Get a reference to the blockdevice at @pathname in the current * Get a reference to the blockdevice at @pathname in the current
* namespace if possible and return it. Return ERR_PTR(error) * namespace if possible and return it. Return ERR_PTR(error)
......
...@@ -195,6 +195,14 @@ check_partition(struct gendisk *hd, struct block_device *bdev) ...@@ -195,6 +195,14 @@ check_partition(struct gendisk *hd, struct block_device *bdev)
return ERR_PTR(res); return ERR_PTR(res);
} }
static ssize_t part_partition_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hd_struct *p = dev_to_part(dev);
return sprintf(buf, "%d\n", p->partno);
}
static ssize_t part_start_show(struct device *dev, static ssize_t part_start_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
...@@ -260,6 +268,7 @@ ssize_t part_fail_store(struct device *dev, ...@@ -260,6 +268,7 @@ ssize_t part_fail_store(struct device *dev,
} }
#endif #endif
static DEVICE_ATTR(partition, S_IRUGO, part_partition_show, NULL);
static DEVICE_ATTR(start, S_IRUGO, part_start_show, NULL); static DEVICE_ATTR(start, S_IRUGO, part_start_show, NULL);
static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL); static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL);
static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL); static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL);
...@@ -269,6 +278,7 @@ static struct device_attribute dev_attr_fail = ...@@ -269,6 +278,7 @@ static struct device_attribute dev_attr_fail =
#endif #endif
static struct attribute *part_attrs[] = { static struct attribute *part_attrs[] = {
&dev_attr_partition.attr,
&dev_attr_start.attr, &dev_attr_start.attr,
&dev_attr_size.attr, &dev_attr_size.attr,
&dev_attr_stat.attr, &dev_attr_stat.attr,
......
...@@ -79,6 +79,13 @@ struct bio { ...@@ -79,6 +79,13 @@ struct bio {
unsigned int bi_size; /* residual I/O count */ unsigned int bi_size; /* residual I/O count */
/*
* To keep track of the max segment size, we account for the
* sizes of the first and last mergeable segments in this bio.
*/
unsigned int bi_seg_front_size;
unsigned int bi_seg_back_size;
unsigned int bi_max_vecs; /* max bvl_vecs we can hold */ unsigned int bi_max_vecs; /* max bvl_vecs we can hold */
unsigned int bi_comp_cpu; /* completion CPU */ unsigned int bi_comp_cpu; /* completion CPU */
......
...@@ -865,7 +865,6 @@ extern void blk_ordered_complete_seq(struct request_queue *, unsigned, int); ...@@ -865,7 +865,6 @@ extern void blk_ordered_complete_seq(struct request_queue *, unsigned, int);
extern int blk_rq_map_sg(struct request_queue *, struct request *, struct scatterlist *); extern int blk_rq_map_sg(struct request_queue *, struct request *, struct scatterlist *);
extern void blk_dump_rq_flags(struct request *, char *); extern void blk_dump_rq_flags(struct request *, char *);
extern void generic_unplug_device(struct request_queue *); extern void generic_unplug_device(struct request_queue *);
extern void __generic_unplug_device(struct request_queue *);
extern long nr_blockdev_pages(void); extern long nr_blockdev_pages(void);
int blk_get_queue(struct request_queue *); int blk_get_queue(struct request_queue *);
......
...@@ -24,6 +24,7 @@ enum blktrace_cat { ...@@ -24,6 +24,7 @@ enum blktrace_cat {
BLK_TC_AHEAD = 1 << 11, /* readahead */ BLK_TC_AHEAD = 1 << 11, /* readahead */
BLK_TC_META = 1 << 12, /* metadata */ BLK_TC_META = 1 << 12, /* metadata */
BLK_TC_DISCARD = 1 << 13, /* discard requests */ BLK_TC_DISCARD = 1 << 13, /* discard requests */
BLK_TC_DRV_DATA = 1 << 14, /* binary per-driver data */
BLK_TC_END = 1 << 15, /* only 16-bits, reminder */ BLK_TC_END = 1 << 15, /* only 16-bits, reminder */
}; };
...@@ -51,6 +52,7 @@ enum blktrace_act { ...@@ -51,6 +52,7 @@ enum blktrace_act {
__BLK_TA_BOUNCE, /* bio was bounced */ __BLK_TA_BOUNCE, /* bio was bounced */
__BLK_TA_REMAP, /* bio was remapped */ __BLK_TA_REMAP, /* bio was remapped */
__BLK_TA_ABORT, /* request aborted */ __BLK_TA_ABORT, /* request aborted */
__BLK_TA_DRV_DATA, /* driver-specific binary data */
}; };
/* /*
...@@ -82,6 +84,7 @@ enum blktrace_notify { ...@@ -82,6 +84,7 @@ enum blktrace_notify {
#define BLK_TA_BOUNCE (__BLK_TA_BOUNCE) #define BLK_TA_BOUNCE (__BLK_TA_BOUNCE)
#define BLK_TA_REMAP (__BLK_TA_REMAP | BLK_TC_ACT(BLK_TC_QUEUE)) #define BLK_TA_REMAP (__BLK_TA_REMAP | BLK_TC_ACT(BLK_TC_QUEUE))
#define BLK_TA_ABORT (__BLK_TA_ABORT | BLK_TC_ACT(BLK_TC_QUEUE)) #define BLK_TA_ABORT (__BLK_TA_ABORT | BLK_TC_ACT(BLK_TC_QUEUE))
#define BLK_TA_DRV_DATA (__BLK_TA_DRV_DATA | BLK_TC_ACT(BLK_TC_DRV_DATA))
#define BLK_TN_PROCESS (__BLK_TN_PROCESS | BLK_TC_ACT(BLK_TC_NOTIFY)) #define BLK_TN_PROCESS (__BLK_TN_PROCESS | BLK_TC_ACT(BLK_TC_NOTIFY))
#define BLK_TN_TIMESTAMP (__BLK_TN_TIMESTAMP | BLK_TC_ACT(BLK_TC_NOTIFY)) #define BLK_TN_TIMESTAMP (__BLK_TN_TIMESTAMP | BLK_TC_ACT(BLK_TC_NOTIFY))
...@@ -317,6 +320,34 @@ static inline void blk_add_trace_remap(struct request_queue *q, struct bio *bio, ...@@ -317,6 +320,34 @@ static inline void blk_add_trace_remap(struct request_queue *q, struct bio *bio,
__blk_add_trace(bt, from, bio->bi_size, bio->bi_rw, BLK_TA_REMAP, !bio_flagged(bio, BIO_UPTODATE), sizeof(r), &r); __blk_add_trace(bt, from, bio->bi_size, bio->bi_rw, BLK_TA_REMAP, !bio_flagged(bio, BIO_UPTODATE), sizeof(r), &r);
} }
/**
* blk_add_driver_data - Add binary message with driver-specific data
* @q: queue the io is for
* @rq: io request
* @data: driver-specific data
* @len: length of driver-specific data
*
* Description:
* Some drivers might want to write driver-specific data per request.
*
**/
static inline void blk_add_driver_data(struct request_queue *q,
struct request *rq,
void *data, size_t len)
{
struct blk_trace *bt = q->blk_trace;
if (likely(!bt))
return;
if (blk_pc_request(rq))
__blk_add_trace(bt, 0, rq->data_len, 0, BLK_TA_DRV_DATA,
rq->errors, len, data);
else
__blk_add_trace(bt, rq->hard_sector, rq->hard_nr_sectors << 9,
0, BLK_TA_DRV_DATA, rq->errors, len, data);
}
extern int blk_trace_setup(struct request_queue *q, char *name, dev_t dev, extern int blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
char __user *arg); char __user *arg);
extern int blk_trace_startstop(struct request_queue *q, int start); extern int blk_trace_startstop(struct request_queue *q, int start);
...@@ -330,6 +361,7 @@ extern int blk_trace_remove(struct request_queue *q); ...@@ -330,6 +361,7 @@ extern int blk_trace_remove(struct request_queue *q);
#define blk_add_trace_generic(q, rq, rw, what) do { } while (0) #define blk_add_trace_generic(q, rq, rw, what) do { } while (0)
#define blk_add_trace_pdu_int(q, what, bio, pdu) do { } while (0) #define blk_add_trace_pdu_int(q, what, bio, pdu) do { } while (0)
#define blk_add_trace_remap(q, bio, dev, f, t) do {} while (0) #define blk_add_trace_remap(q, bio, dev, f, t) do {} while (0)
#define blk_add_driver_data(q, rq, data, len) do {} while (0)
#define do_blk_trace_setup(q, name, dev, buts) (-ENOTTY) #define do_blk_trace_setup(q, name, dev, buts) (-ENOTTY)
#define blk_trace_setup(q, name, dev, arg) (-ENOTTY) #define blk_trace_setup(q, name, dev, arg) (-ENOTTY)
#define blk_trace_startstop(q, start) (-ENOTTY) #define blk_trace_startstop(q, start) (-ENOTTY)
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
#include <linux/hardirq.h> #include <linux/hardirq.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/irqflags.h> #include <linux/irqflags.h>
#include <linux/smp.h>
#include <linux/percpu.h>
#include <asm/atomic.h> #include <asm/atomic.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/system.h> #include <asm/system.h>
...@@ -273,6 +275,25 @@ extern void softirq_init(void); ...@@ -273,6 +275,25 @@ extern void softirq_init(void);
extern void raise_softirq_irqoff(unsigned int nr); extern void raise_softirq_irqoff(unsigned int nr);
extern void raise_softirq(unsigned int nr); extern void raise_softirq(unsigned int nr);
/* This is the worklist that queues up per-cpu softirq work.
*
* send_remote_sendirq() adds work to these lists, and
* the softirq handler itself dequeues from them. The queues
* are protected by disabling local cpu interrupts and they must
* only be accessed by the local cpu that they are for.
*/
DECLARE_PER_CPU(struct list_head [NR_SOFTIRQS], softirq_work_list);
/* Try to send a softirq to a remote cpu. If this cannot be done, the
* work will be queued to the local cpu.
*/
extern void send_remote_softirq(struct call_single_data *cp, int cpu, int softirq);
/* Like send_remote_softirq(), but the caller must disable local cpu interrupts
* and compute the current cpu, passed in as 'this_cpu'.
*/
extern void __send_remote_softirq(struct call_single_data *cp, int cpu,
int this_cpu, int softirq);
/* Tasklets --- multithreaded analogue of BHs. /* Tasklets --- multithreaded analogue of BHs.
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
...@@ -16,7 +17,8 @@ struct call_single_data { ...@@ -16,7 +17,8 @@ struct call_single_data {
struct list_head list; struct list_head list;
void (*func) (void *info); void (*func) (void *info);
void *info; void *info;
unsigned int flags; u16 flags;
u16 priv;
}; };
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
* Distribute under GPLv2. * Distribute under GPLv2.
* *
* Rewritten. Old one was good in 2.2, but in 2.3 it was immoral. --ANK (990903) * Rewritten. Old one was good in 2.2, but in 2.3 it was immoral. --ANK (990903)
*
* Remote softirq infrastructure is by Jens Axboe.
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -474,17 +476,144 @@ void tasklet_kill(struct tasklet_struct *t) ...@@ -474,17 +476,144 @@ void tasklet_kill(struct tasklet_struct *t)
EXPORT_SYMBOL(tasklet_kill); EXPORT_SYMBOL(tasklet_kill);
DEFINE_PER_CPU(struct list_head [NR_SOFTIRQS], softirq_work_list);
EXPORT_PER_CPU_SYMBOL(softirq_work_list);
static void __local_trigger(struct call_single_data *cp, int softirq)
{
struct list_head *head = &__get_cpu_var(softirq_work_list[softirq]);
list_add_tail(&cp->list, head);
/* Trigger the softirq only if the list was previously empty. */
if (head->next == &cp->list)
raise_softirq_irqoff(softirq);
}
#ifdef CONFIG_USE_GENERIC_SMP_HELPERS
static void remote_softirq_receive(void *data)
{
struct call_single_data *cp = data;
unsigned long flags;
int softirq;
softirq = cp->priv;
local_irq_save(flags);
__local_trigger(cp, softirq);
local_irq_restore(flags);
}
static int __try_remote_softirq(struct call_single_data *cp, int cpu, int softirq)
{
if (cpu_online(cpu)) {
cp->func = remote_softirq_receive;
cp->info = cp;
cp->flags = 0;
cp->priv = softirq;
__smp_call_function_single(cpu, cp);
return 0;
}
return 1;
}
#else /* CONFIG_USE_GENERIC_SMP_HELPERS */
static int __try_remote_softirq(struct call_single_data *cp, int cpu, int softirq)
{
return 1;
}
#endif
/**
* __send_remote_softirq - try to schedule softirq work on a remote cpu
* @cp: private SMP call function data area
* @cpu: the remote cpu
* @this_cpu: the currently executing cpu
* @softirq: the softirq for the work
*
* Attempt to schedule softirq work on a remote cpu. If this cannot be
* done, the work is instead queued up on the local cpu.
*
* Interrupts must be disabled.
*/
void __send_remote_softirq(struct call_single_data *cp, int cpu, int this_cpu, int softirq)
{
if (cpu == this_cpu || __try_remote_softirq(cp, cpu, softirq))
__local_trigger(cp, softirq);
}
EXPORT_SYMBOL(__send_remote_softirq);
/**
* send_remote_softirq - try to schedule softirq work on a remote cpu
* @cp: private SMP call function data area
* @cpu: the remote cpu
* @softirq: the softirq for the work
*
* Like __send_remote_softirq except that disabling interrupts and
* computing the current cpu is done for the caller.
*/
void send_remote_softirq(struct call_single_data *cp, int cpu, int softirq)
{
unsigned long flags;
int this_cpu;
local_irq_save(flags);
this_cpu = smp_processor_id();
__send_remote_softirq(cp, cpu, this_cpu, softirq);
local_irq_restore(flags);
}
EXPORT_SYMBOL(send_remote_softirq);
static int __cpuinit remote_softirq_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
/*
* If a CPU goes away, splice its entries to the current CPU
* and trigger a run of the softirq
*/
if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) {
int cpu = (unsigned long) hcpu;
int i;
local_irq_disable();
for (i = 0; i < NR_SOFTIRQS; i++) {
struct list_head *head = &per_cpu(softirq_work_list[i], cpu);
struct list_head *local_head;
if (list_empty(head))
continue;
local_head = &__get_cpu_var(softirq_work_list[i]);
list_splice_init(head, local_head);
raise_softirq_irqoff(i);
}
local_irq_enable();
}
return NOTIFY_OK;
}
static struct notifier_block __cpuinitdata remote_softirq_cpu_notifier = {
.notifier_call = remote_softirq_cpu_notify,
};
void __init softirq_init(void) void __init softirq_init(void)
{ {
int cpu; int cpu;
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
int i;
per_cpu(tasklet_vec, cpu).tail = per_cpu(tasklet_vec, cpu).tail =
&per_cpu(tasklet_vec, cpu).head; &per_cpu(tasklet_vec, cpu).head;
per_cpu(tasklet_hi_vec, cpu).tail = per_cpu(tasklet_hi_vec, cpu).tail =
&per_cpu(tasklet_hi_vec, cpu).head; &per_cpu(tasklet_hi_vec, cpu).head;
for (i = 0; i < NR_SOFTIRQS; i++)
INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));
} }
register_hotcpu_notifier(&remote_softirq_cpu_notifier);
open_softirq(TASKLET_SOFTIRQ, tasklet_action); open_softirq(TASKLET_SOFTIRQ, tasklet_action);
open_softirq(HI_SOFTIRQ, tasklet_hi_action); open_softirq(HI_SOFTIRQ, tasklet_hi_action);
} }
......
...@@ -652,6 +652,11 @@ config DEBUG_BLOCK_EXT_DEVT ...@@ -652,6 +652,11 @@ config DEBUG_BLOCK_EXT_DEVT
depends on BLOCK depends on BLOCK
default n default n
help help
BIG FAT WARNING: ENABLING THIS OPTION MIGHT BREAK BOOTING ON
SOME DISTRIBUTIONS. DO NOT ENABLE THIS UNLESS YOU KNOW WHAT
YOU ARE DOING. Distros, please enable this and fix whatever
is broken.
Conventionally, block device numbers are allocated from Conventionally, block device numbers are allocated from
predetermined contiguous area. However, extended block area predetermined contiguous area. However, extended block area
may introduce non-contiguous block device numbers. This may introduce non-contiguous block device numbers. This
......
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