diff -urpN 7-procfs-optional/Documentation/ABI/testing/sysfs-class-pktcdvd 8-wqueue-congestion/Documentation/ABI/testing/sysfs-class-pktcdvd --- 7-procfs-optional/Documentation/ABI/testing/sysfs-class-pktcdvd 2006-10-03 13:17:15.000000000 +0200 +++ 8-wqueue-congestion/Documentation/ABI/testing/sysfs-class-pktcdvd 2006-10-03 15:46:49.000000000 +0200 @@ -38,6 +38,23 @@ these files in the sysfs: reset (0200) Write any value to it to reset pktcdvd device statistic values, like bytes read/written. + +/sys/class/pktcdvd/pktcdvd[0-7]/write_queue/ + size (0444) Contains the size of the bio write + queue. + + congestion_off (0644) If bio write queue size is below + this mark, accept new bio requests + from the block layer. + + congestion_on (0644) If bio write queue size is higher + as this mark, do no longer accept + bio write requests from the block + layer and wait till the pktcdvd + device has processed enough bio's + so that bio write queue size is + below congestion off mark. + Example: diff -urpN 7-procfs-optional/Documentation/cdrom/packet-writing.txt 8-wqueue-congestion/Documentation/cdrom/packet-writing.txt --- 7-procfs-optional/Documentation/cdrom/packet-writing.txt 2006-10-03 13:17:15.000000000 +0200 +++ 8-wqueue-congestion/Documentation/cdrom/packet-writing.txt 2006-10-03 15:52:00.000000000 +0200 @@ -125,8 +125,26 @@ To read pktcdvd device infos in human re For a description of the debugfs interface look into the file: Documentation/ABI/testing/debugfs-pktcdvd - - + + +Bio write queue congestion marks +-------------------------------- +The pktcdvd driver allows to adjust the behaviour of the +internal bio write queue. +This can be done with the two write_congestion_[on|off] marks. +The driver does only accept up to write_congestion_on bio +write request from the i/o block layer, and waits till the +requests are processed by the mapped block device and +the queue size is below the write_congestion_off mark. +In previous versions of pktcdvd, the driver accepted all +incoming bio write request. This led sometimes to kernel +out of memory oops (maybe some bugs in the linux kernel ;) +CAUTION: use this options only if you know what you do! +The default settings for the congestion marks should be ok. +The maximum size of the bio write queue can influence +driver's cpu load and memory consumption. + + Links ----- diff -urpN 7-procfs-optional/drivers/block/pktcdvd.c 8-wqueue-congestion/drivers/block/pktcdvd.c --- 7-procfs-optional/drivers/block/pktcdvd.c 2006-10-03 13:54:31.000000000 +0200 +++ 8-wqueue-congestion/drivers/block/pktcdvd.c 2006-10-03 13:54:43.000000000 +0200 @@ -88,6 +88,8 @@ static struct pktcdvd_device *pkt_devs[MAX_WRITERS]; static int pktdev_major = 0; /* default: dynamic major number */ +static int write_congestion_on = PKT_WRITE_CONGESTION_ON; +static int write_congestion_off = PKT_WRITE_CONGESTION_OFF; static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */ static mempool_t *psd_pool; @@ -133,6 +135,23 @@ static struct pktcdvd_device *pkt_find_d return NULL; } +static void init_write_congestion_marks(int* lo, int* hi) +{ + if (*hi > 0) { + *hi = max(*hi, PKT_WRITE_CONGESTION_MIN); + *hi = min(*hi, PKT_WRITE_CONGESTION_MAX); + if (*lo <= 0) + *lo = *hi - PKT_WRITE_CONGESTION_THRESHOLD; + else { + *lo = min(*lo, *hi - PKT_WRITE_CONGESTION_THRESHOLD); + *lo = max(*lo, PKT_WRITE_CONGESTION_MIN/4); + } + } else { + *hi = -1; + *lo = -1; + } +} + static void pkt_count_states(struct pktcdvd_device *pd, int *states) { struct packet_data *pkt; @@ -207,6 +226,10 @@ static int pkt_print_info(struct pktcdvd pkt_count_states(pd, states); PRINT("\tstate:\t\t\ti:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n", states[0], states[1], states[2], states[3], states[4], states[5]); + + PRINT("\twrite congestion marks:\toff=%d on=%d\n", + pd->write_congestion_off, + pd->write_congestion_on); #undef PRINT buf[blen-1] = 0; return n; @@ -262,6 +285,9 @@ static void pkt_kobj_release(struct kobj /********************************************************** /sys/class/pktcdvd/pktcdvd[0-7]/ + write_queue/size + write_queue/congestion_off + write_queue/congestion_on stat/reset stat/packets_started stat/packets_finished @@ -270,6 +296,17 @@ static void pkt_kobj_release(struct kobj stat/kb_read_gather **********************************************************/ +DEF_ATTR(kobj_pkt_attr_wq1, "size", 0444); +DEF_ATTR(kobj_pkt_attr_wq2, "congestion_off", 0644); +DEF_ATTR(kobj_pkt_attr_wq3, "congestion_on", 0644); + +static struct attribute *kobj_pkt_attrs_wqueue[] = { + &kobj_pkt_attr_wq1, + &kobj_pkt_attr_wq2, + &kobj_pkt_attr_wq3, + NULL +}; + DEF_ATTR(kobj_pkt_attr_st1, "reset", 0200); DEF_ATTR(kobj_pkt_attr_st2, "packets_started", 0444); DEF_ATTR(kobj_pkt_attr_st3, "packets_finished", 0444); @@ -302,6 +339,7 @@ static ssize_t kobj_pkt_show(struct kobj { struct pktcdvd_device *pd; int n = 0; + int v; data[0] = 0; pd = to_pktcdvdkobj(kobj)->pd; @@ -320,6 +358,24 @@ static ssize_t kobj_pkt_show(struct kobj } else if (strcmp(attr->name, "kb_read_gather") == 0) { n = sprintf(data, "%lu\n", pd->stats.secs_rg >> 1); + + } else if (strcmp(attr->name, "size") == 0) { + spin_lock(&pd->lock); + v = pd->bio_queue_size; + spin_unlock(&pd->lock); + n = sprintf(data, "%d\n", v); + + } else if (strcmp(attr->name, "congestion_off") == 0) { + spin_lock(&pd->lock); + v = pd->write_congestion_off; + spin_unlock(&pd->lock); + n = sprintf(data, "%d\n", v); + + } else if (strcmp(attr->name, "congestion_on") == 0) { + spin_lock(&pd->lock); + v = pd->write_congestion_on; + spin_unlock(&pd->lock); + n = sprintf(data, "%d\n", v); } return n; } @@ -328,6 +384,7 @@ static ssize_t kobj_pkt_store(struct kob struct attribute *attr, const char *data, size_t len) { + int val; struct pktcdvd_device *pd; DECLARE_BUF_AS_STRING(dbuf, data, len); /* ensure sscanf scans a string */ @@ -338,8 +395,24 @@ static ssize_t kobj_pkt_store(struct kob pd->stats.pkt_ended = 0; pd->stats.secs_w = 0; pd->stats.secs_rg = 0; - pd->stats.secs_r = 0; + pd->stats.secs_r = 0; + + } else if (strcmp(attr->name, "congestion_off") == 0 + && sscanf(dbuf, "%d", &val) == 1) { + spin_lock(&pd->lock); + pd->write_congestion_off = val; + init_write_congestion_marks(&pd->write_congestion_off, + &pd->write_congestion_on); + spin_unlock(&pd->lock); + } else if (strcmp(attr->name, "congestion_on") == 0 + && sscanf(dbuf, "%d", &val) == 1) { + spin_lock(&pd->lock); + pd->write_congestion_on = val; + init_write_congestion_marks(&pd->write_congestion_off, + &pd->write_congestion_on); + spin_unlock(&pd->lock); } + return len; } @@ -352,6 +425,12 @@ static struct kobj_type kobj_pkt_type_st .sysfs_ops = &kobj_pkt_ops, .default_attrs = kobj_pkt_attrs_stat }; +static struct kobj_type kobj_pkt_type_wqueue = { + .release = pkt_kobj_release, + .sysfs_ops = &kobj_pkt_ops, + .default_attrs = kobj_pkt_attrs_wqueue +}; + static void pkt_sysfs_dev_new(struct pktcdvd_device *pd) { @@ -366,12 +445,16 @@ static void pkt_sysfs_dev_new(struct pkt pd->kobj_stat = pkt_kobj_create(pd, "stat", &pd->clsdev->kobj, &kobj_pkt_type_stat); + pd->kobj_wqueue = pkt_kobj_create(pd, "write_queue", + &pd->clsdev->kobj, + &kobj_pkt_type_wqueue); } } static void pkt_sysfs_dev_remove(struct pktcdvd_device *pd) { pkt_kobj_remove(pd->kobj_stat); + pkt_kobj_remove(pd->kobj_wqueue); if (class_pktcdvd) class_device_destroy(class_pktcdvd, pd->pkt_dev); } @@ -1460,6 +1543,7 @@ static int pkt_handle_queue(struct pktcd sector_t zone = 0; /* Suppress gcc warning */ struct pkt_rb_node *node, *first_node; struct rb_node *n; + int wakeup; VPRINTK("handle_queue\n"); @@ -1532,7 +1616,14 @@ try_next_bio: pkt->write_size += bio->bi_size / CD_FRAMESIZE; spin_unlock(&pkt->lock); } + /* check write congestion marks, and if bio_queue_size is + below, wake up any waiters */ + wakeup = (pd->write_congestion_on > 0 + && pd->bio_queue_size <= pd->write_congestion_off + && waitqueue_active(&pd->write_congestion_wqueue)); spin_unlock(&pd->lock); + if (wakeup) + wake_up(&pd->write_congestion_wqueue); pkt->sleep_time = max(PACKET_WAIT_TIME, 1); pkt_set_state(pkt, PACKET_WAITING_STATE); @@ -2724,6 +2815,32 @@ static int pkt_make_request(request_queu spin_unlock(&pd->cdrw.active_list_lock); /* + * Test if there is enough room left in the bio work queue + * (queue size >= congestion on mark). + * If not, wait till the work queue size is below the congestion off mark. + * This is similar to the get_request_wait() call made in the block + * layer function __make_request() used for normal block i/o request + * handling. + */ + spin_lock(&pd->lock); + if (pd->write_congestion_on > 0 + && pd->bio_queue_size >= pd->write_congestion_on) { + DEFINE_WAIT(wait); + /* wait till number of bio requests is low enough */ + do { + spin_unlock(&pd->lock); + prepare_to_wait_exclusive(&pd->write_congestion_wqueue, + &wait, TASK_UNINTERRUPTIBLE); + io_schedule(); + /* if we are here, bio_queue_size should be below + congestion_off, but be sure and do a test */ + spin_lock(&pd->lock); + } while(pd->bio_queue_size > pd->write_congestion_off); + finish_wait(&pd->write_congestion_wqueue, &wait); + } + spin_unlock(&pd->lock); + + /* * No matching packet found. Store the bio in the work queue. */ node = mempool_alloc(pd->rb_pool, GFP_NOIO); @@ -2951,6 +3068,10 @@ static int pkt_setup_dev(dev_t dev, dev_ init_waitqueue_head(&pd->wqueue); pd->bio_queue = RB_ROOT; + init_waitqueue_head(&pd->write_congestion_wqueue); + pd->write_congestion_on = write_congestion_on; + pd->write_congestion_off = write_congestion_off; + disk = alloc_disk(1); if (!disk) goto out_mem; @@ -3057,6 +3178,9 @@ static int __init pkt_init(void) { int ret; + init_write_congestion_marks(&write_congestion_off, + &write_congestion_on); + mutex_init(&ctl_mutex); psd_pool = mempool_create_kmalloc_pool(PSD_POOL_SIZE, diff -urpN 7-procfs-optional/include/linux/pktcdvd.h 8-wqueue-congestion/include/linux/pktcdvd.h --- 7-procfs-optional/include/linux/pktcdvd.h 2006-10-03 13:09:18.000000000 +0200 +++ 8-wqueue-congestion/include/linux/pktcdvd.h 2006-10-03 13:37:01.000000000 +0200 @@ -123,6 +123,14 @@ struct pkt_ctrl_command { #endif +/* default bio write queue congestion marks */ +#define PKT_WRITE_CONGESTION_ON 5000 +#define PKT_WRITE_CONGESTION_OFF 4500 +#define PKT_WRITE_CONGESTION_MAX (1024*1024) +#define PKT_WRITE_CONGESTION_MIN 100 +#define PKT_WRITE_CONGESTION_THRESHOLD 25 + + struct packet_settings { __u32 size; /* packet size in (512 byte) sectors */ @@ -291,7 +299,11 @@ struct pktcdvd_device struct packet_iosched iosched; struct gendisk *disk; - + + wait_queue_head_t write_congestion_wqueue; + int write_congestion_off; + int write_congestion_on; + struct class_device *clsdev; /* sysfs pktcdvd[0-7] class dev */ struct pktcdvd_kobj *kobj_stat; /* sysfs pktcdvd[0-7]/stat/ */ struct pktcdvd_kobj *kobj_wqueue; /* sysfs pktcdvd[0-7]/write_queue/ */