Hi,

something like this?

A callback list in the queue.

The struct congestion_notify could be added to the struct mapped_device.
Two atomic_t read_congested and write_congested. When it changed from/to
zero set/clear the corresponding bit in q->backing_dev_info?


--- linux-2.6.4-rc1.orig/drivers/block/ll_rw_blk.c	2004-02-29 20:41:56.000000000 +0100
+++ linux/drivers/block/ll_rw_blk.c	2004-03-01 22:32:23.782708544 +0100
@@ -93,6 +93,8 @@
 	return ret;
 }
 
+static SPIN_LOCK_UNLOCKED(congestion_notify_lock);
+
 /*
  * A queue has just exitted congestion.  Note this in the global counter of
  * congested queues, and wake up anyone who was waiting for requests to be
@@ -102,6 +104,13 @@
 {
 	enum bdi_state bit;
 	wait_queue_head_t *wqh = &congestion_wqh[rw];
+	struct congested_notify *notify;
+	unsigned long flags;
+
+	spin_lock_irqsave(&congestion_notify_lock, flags);
+	list_for_each_entry(notify, &q->congested_notify, list)
+		notify->callback(q, notify->private, 1, rw);
+	spin_unlock_irqrestore(&congestion_notify_lock, flags);
 
 	bit = (rw == WRITE) ? BDI_write_congested : BDI_read_congested;
 	clear_bit(bit, &q->backing_dev_info.state);
@@ -116,6 +125,13 @@
 static void set_queue_congested(request_queue_t *q, int rw)
 {
 	enum bdi_state bit;
+	struct congested_notify *notify;
+	unsigned long flags;
+
+	spin_lock_irqsave(&congestion_notify_lock, flags);
+	list_for_each_entry(notify, &q->congested_notify, list)
+		notify->callback(q, notify->private, 0, rw);
+	spin_unlock_irqrestore(&congestion_notify_lock, flags);
 
 	bit = (rw == WRITE) ? BDI_write_congested : BDI_read_congested;
 	set_bit(bit, &q->backing_dev_info.state);
@@ -140,6 +156,22 @@
 	return ret;
 }
 
+void blk_queue_add_congestion_notify(request_queue_t *q,
+				     struct congestion_notify *notify)
+{
+	spin_lock_irq(&congestion_notify_lock);
+	list_add(&q->congestion_notify, &notify->list);
+	spin_unlock_irq(&congestion_notify_lock);
+}
+
+void blk_queue_remove_congestion_notify(request_queue_t *q,
+					struct congestion_notify *notify)
+{
+	spin_lock_irq(&congestion_notify_lock);
+	list_del(&notify->list);
+	spin_unlock_irq(&congestion_notify_lock);
+}
+
 void blk_queue_activity_fn(request_queue_t *q, activity_fn *fn, void *data)
 {
 	q->activity_fn = fn;
@@ -243,6 +275,7 @@
 	blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH);
 
 	INIT_LIST_HEAD(&q->plug_list);
+	INIT_LIST_HEAD(&q->congestion_notify);
 
 	blk_queue_activity_fn(q, NULL, NULL);
 }
--- linux-2.6.4-rc1.orig/include/linux/blkdev.h	2004-02-29 20:41:58.000000000 +0100
+++ linux/include/linux/blkdev.h	2004-03-01 22:11:29.101449088 +0100
@@ -248,6 +248,8 @@
 struct bio_vec;
 typedef int (merge_bvec_fn) (request_queue_t *, struct bio *, struct bio_vec *);
 typedef void (activity_fn) (void *data, int rw);
+typedef void (congestion_callback_fn) (struct request_queue *q, void *data,
+				       int clear, int rw);
 
 enum blk_queue_state {
 	Queue_down,
@@ -267,6 +269,12 @@
 	atomic_t refcnt;		/* map can be shared */
 };
 
+struct congestion_notify {
+	struct list_head list;
+	congestion_callback_fn callback;
+	void *private;
+};
+
 struct request_queue
 {
 	/*
@@ -301,6 +309,8 @@
 
 	struct backing_dev_info	backing_dev_info;
 
+	list_head		congestion_notify;
+
 	/*
 	 * The queue owner gets to use this for whatever they like.
 	 * ll_rw_blk doesn't touch it.
@@ -563,6 +573,8 @@
 extern void blk_queue_merge_bvec(request_queue_t *, merge_bvec_fn *);
 extern void blk_queue_dma_alignment(request_queue_t *, int);
 extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev);
+extern void blk_queue_add_congestion_notify(request_queue_t *, struct congestion_notify *);
+extern void blk_queue_remove_congestion_notify(request_queue_t *, struct congestion_notify *);
 
 extern int blk_rq_map_sg(request_queue_t *, struct request *, struct scatterlist *);
 extern void blk_dump_rq_flags(struct request *, char *);
