reworked IDE/general tagged command queueing
PATCH 1/1 reworked IDE/general tagged command queueing
Hi,
I've rewritten parts of the IDE TCQ stuff to be, well, a lot better in my oppinion. I had to accept that the ata_request and rq->special usage sucked, it was just one big mess.
So following a suggestion from Martin and Linus, I implemented some basic tagged command queueing back end in the block layer. This is what the new IDE TCQ core is build on, and what potentially others can use as well. I'll start by describing the new API:
int blk_queue_init_tags(request_queue_t *q, int depth)
Setup queueing structures for the request queue and depth given.
This will setup q->queue_tags, which is a struct blk_queue_tag:
struct blk_queue_tag {
struct request **tag_index; /* map of busy tags */
unsigned long *tag_map; /* bit map of free/busy tags */
struct list_head busy_list; /* fifo list of busy tags */
int busy; /* current depth */
int max_depth;
};
tag_index[] holds pointers to the active requests, and tag_mapis a bitmap of busy tags. The rest should not need any explanation.
void blk_queue_free_tags(request_queue_t *q)
Tear down the tag info setup by blk_queue_init_tags(). This will be done automatically when blk_cleanup_queue() is called, so there's usually no need to call this directly (unless you just want to disable tagging and let the queue live on).
int blk_queue_start_tag(request_queue_t *q, struct request *rq)
Start a new tag associated with struct request. This will grab an available tag number and add it to the internal structures.
void blk_queue_end_tag(request_queue_t *q, struct request *rq)
End this tag. Typically called when end_that_request_first() says the request is done, before calling end_that_request_last().
void blk_queue_invalidate_tags(request_queue_t *q)
Called by driver to clear our internal tag queue and readd the requests to the request queue. Submission order is preserved, so should be safe for barriers too.
That's it. For an example of usage, see the IDE TCQ patch attached as well. The API can of course be extended if the need arises, I've kept it simple on purpose since it was all I needed for the IDE parts -- and it's silly to add features just because you _think_ someone might need them.
Now on to the IDE parts. The TCQ patch is now a LOT less invasive than before. With the block tagged features in place, there's no need for a private request type in the IDE layer -- struct request is just fine. Changes to IDE core (ide.c) are now extremely small. Changes to ide-disk are minute as well, most of it is actually just the proc file stuff. ide-dma changes are mainly needed because otherwise ide-tcq would have to duplicate the ide_start_dma() functionality on its own. ide-taskfile changes are just to detect the new commands possibly (read queued etc) and a spelling fix :-). There is one ugly hack in ide-taskfile, that will disappear once the dmaproc parts are split in two as outlined in the FIXME there.
So give it a whirl, patches is against 2.5.12. block-tag-4 can be applied independently, ide-tag-2 relies on it of course.
Note that hdparm-4.7 and later has support for enabling and disabling tcq/queue depth, it's done with the -Q parameter:
bart:~ # hdparm -Q0 /dev/hdc
/dev/hdc:
setting using_dma_q to 0 (off)
using_dma_q = 0 (off)
bart:~ # hdparm -Q32 /dev/hdc
/dev/hdc:
setting using_dma_q to 32 (on)
using_dma_q = 1 (on)
and so forth :-)
Find it here:
http://www.ibiblio.org/pub/Linux/system/hardware/
Signature · 1 line
Jens Axboe
# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas:
# ChangeSet 1.569 -> 1.570
# drivers/block/ll_rw_blk.c 1.64 -> 1.65
# include/linux/blkdev.h 1.43 -> 1.44
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/05/01 [email protected] 1.570
# Initial block level tagged command queueing helpers
# --------------------------------------------
#
drivers/block/ll_rw_blk.c +225 -4
@@ -311,9 +311,221 @@ q->queue_lock = lock; } +/** +* blk_queue_free_tags - release tag maintenance info +* @q: the request queue for the device +* +* Notes: +* blk_cleanup_queue() will take care of calling this function, if tagging +* has been used. So there's usually no need to call this directly, unless +* tagging is just being disabled but the queue remains in function. +**/ +void blk_queue_free_tags (request_queue_t *q) +{ +struct blk_queue_tag *bqt = q->queue_tags; + +if (!bqt) +return ; + +BUG_ON (bqt->busy); +BUG_ON (!list_empty (&bqt->busy_list)); + +kfree (bqt->tag_index); + bqt->tag_index =NULL ; + +kfree (bqt->tag_map); + bqt->tag_map =NULL ; + +kfree (bqt); + q->queue_tags =NULL ; + q->queue_flags &= ~(1 <<QUEUE_FLAG_QUEUED ); +} + +/** +* blk_queue_init_tags - initialize the queue tag info +* @q: the request queue for the device +* @depth: the maximum queue depth supported +**/ +int blk_queue_init_tags (request_queue_t *q,int depth) +{ +struct blk_queue_tag *tags; +int bits; + +if (depth > queue_nr_requests) + depth = queue_nr_requests; + + tags =kmalloc (sizeof (struct blk_queue_tag ),GFP_ATOMIC ); +if (!tags) + goto fail; + + tags->tag_index =kmalloc (depth *sizeof (struct request *),GFP_ATOMIC ); +if (!tags->tag_index) + goto fail_index; + + bits = depth /BLK_TAGS_PER_LONG ; + tags->tag_map =kmalloc (bits *sizeof (unsigned long ),GFP_ATOMIC ); +if (!tags->tag_map) + goto fail_map; + +memset (tags->tag_index, depth *sizeof (struct request *),0 ); +memset (tags->tag_map, bits *sizeof (unsigned long ),0 ); +INIT_LIST_HEAD (&tags->busy_list); + tags->busy =0 ; + tags->max_depth = depth; + +/* +* assign it, all done +*/ + q->queue_tags = tags; + q->queue_flags |= (1 <<QUEUE_FLAG_QUEUED ); +return 0 ; + +fail_map: +kfree (tags->tag_index); +fail_index: +kfree (tags); +fail: +return -ENOMEM ; +} + +/* +* queue_lock must be held for both get_tag and clear_tag +*/ +static int blk_queue_get_tag (struct blk_queue_tag *bqt) +{ +unsigned long *map; +int i, depth, tag, bit; + + tag =-1 ; + depth = i =0 ; +do { + map = &bqt->tag_map[i++]; +if (*map !=-1UL ) +break ; + + depth +=BLK_TAGS_PER_LONG ; +if (depth < bqt->max_depth) +continue ; + + map =NULL ; + }while (map); + +if (map) { + bit =ffz (*map); +if (bit + depth <= bqt->max_depth) { +__set_bit (bit, map); + tag = bit + depth; + } + } + +return tag; +} + +/** +* blk_queue_end_tag - end tag operations for a request +* @q: the request queue for the device +* @tag: the tag that has completed +* +* Description: +* Typically called when end_that_request_first() returns 0, meaning +* all transfers have been done for a request. It's important to call +* this function before end_that_request_last(), as that will put the +* request back on the free list thus corrupting the internal tag list. +* +* Notes: +* queue lock must be held. +**/ +void blk_queue_end_tag (request_queue_t *q,struct request *rq) +{ +struct blk_queue_tag *bqt = q->queue_tags; +int tag = rq->tag; + +BUG_ON (tag ==-1 ); + +if (unlikely (!__test_and_clear_bit (tag, bqt->tag_map))) { +printk ("attempt to clear non-busy tag (%d)\n" , tag); +return ; + } + +list_del (&rq->queuelist); + rq->flags &= ~REQ_QUEUED ; + rq->tag =-1 ; + +if (unlikely (bqt->tag_index[tag] ==NULL )) +printk ("tag %d is missing\n" , tag); + + bqt->tag_index[tag] =NULL ; + bqt->busy--; +} + +/** +* blk_queue_start_tag - find a free tag and assign it +* @q: the request queue for the device +* @rq: the block request that needs tagging +* +* Description: +* This can either be used as a stand-alone helper, or possibly be +* assigned as the queue &prep_rq_fn (in which case &struct request +* automagically gets a tag assigned). Note that this function assumes +* that only REQ_CMD requests can be queued! The request will also be +* removed from the request queue, so it's the drivers responsibility to +* readd it if it should need to be restarted for some reason. +* +* Notes: +* queue lock must be held. +**/ +int blk_queue_start_tag (request_queue_t *q,struct request *rq) +{ +struct blk_queue_tag *bqt = q->queue_tags; +int tag; + +if (unlikely (!(rq->flags &REQ_CMD ))) +return 1 ; + + tag =blk_queue_get_tag (bqt); +if (tag !=-1 ) { + rq->flags |=REQ_QUEUED ; + rq->tag = tag; + bqt->tag_index[tag] = rq; +blkdev_dequeue_request (rq); +list_add (&rq->queuelist, &bqt->busy_list); + bqt->busy++; +return 0 ; + } + +return 1 ; +} + +/** +* blk_queue_invalidate_tags - invalidate all pending tags +* @q: the request queue for the device +* +* Description: +* Hardware conditions make dictate a need to stop all pending request. +* In this case, we will safely clear the block side of the tag queue and +* readd all request to the request queue in the right order. +* +* Notes: +* queue lock must be held. +**/ +void blk_queue_invalidate_tags (request_queue_t *q) +{ +struct blk_queue_tag *bqt = q->queue_tags; +struct list_head *tmp; +struct request *rq; + +list_for_each (tmp, &bqt->busy_list) { + rq =list_entry_rq (tmp); + +blk_queue_end_tag (q, rq); + rq->flags &= ~REQ_STARTED ; +elv_add_request (q, rq,0 ); + } +} +static char *rq_flags[] = {"REQ_RW" ,"REQ_RW_AHEAD" ,"REQ_BARRIER" ,"REQ_CMD" ,"REQ_NOMERGE" ,"REQ_STARTED" , -"REQ_DONTPREP" ,"REQ_DRIVE_CMD" , +"REQ_DONTPREP" ,"REQ_QUEUED" ,"REQ_DRIVE_CMD" ,"REQ_DRIVE_ACB" ,"REQ_PC" ,"REQ_BLOCK_PC" ,"REQ_SENSE" ,"REQ_SPECIAL" };
@@ -619,7 +831,7 @@ total_hw_segments = req->nr_hw_segments + next->nr_hw_segments;if (blk_hw_contig_segment (q, req->biotail, next->bio)) total_hw_segments--; - +if (total_hw_segments > q->max_hw_segments)return 0 ;
@@ -719,7 +931,7 @@* when a block device is being de-registered. Currently, its * primary task it to free all the &struct request structures that * were allocated to the queue. -* Caveat: +* Caveat: * Hopefully the low level driver will have finished any * outstanding requests first... **/
@@ -733,6 +945,9 @@if (count)printk ("blk_cleanup_queue: leaked requests (%d)\n" , count); +if (blk_queue_tagged (q)) +blk_queue_free_tags (q); +elevator_exit (q, &q->elevator);memset (q,0 ,sizeof (*q));
@@ -1599,7 +1814,7 @@* Description: * Ends I/O on the first buffer attached to @req, and sets it up * for the next buffer_head (if any) in the cluster. -* +* * Return: * 0 - we are done with this request, call end_that_request_last() * 1 - still buffers pending for this request
@@ -1753,3 +1968,9 @@EXPORT_SYMBOL (ll_10byte_cmd_build);EXPORT_SYMBOL (blk_queue_prep_rq); + +EXPORT_SYMBOL (blk_queue_init_tags); +EXPORT_SYMBOL (blk_queue_free_tags); +EXPORT_SYMBOL (blk_queue_start_tag); +EXPORT_SYMBOL (blk_queue_end_tag); +EXPORT_SYMBOL (blk_queue_invalidate_tags);
include/linux/blkdev.h +31 -0
@@ -56,6 +56,7 @@unsigned int current_nr_sectors;unsigned int hard_cur_sectors; +int tag;void *special;char *buffer;struct completion *waiting;
@@ -75,6 +76,7 @@ __REQ_NOMERGE,/* don't touch this for merging */ __REQ_STARTED,/* drive already may have started this one */ __REQ_DONTPREP,/* don't call prep for this one */ + __REQ_QUEUED, /* uses queueing *//* * for ATA/ATAPI devices */
@@ -97,6 +99,7 @@#define REQ_NOMERGE (1 << __REQ_NOMERGE)#define REQ_STARTED (1 << __REQ_STARTED)#define REQ_DONTPREP (1 << __REQ_DONTPREP) +#define REQ_QUEUED (1 << __REQ_QUEUED)#define REQ_DRIVE_CMD (1 << __REQ_DRIVE_CMD)#define REQ_DRIVE_ACB (1 << __REQ_DRIVE_ACB)#define REQ_PC (1 << __REQ_PC)
@@ -121,6 +124,17 @@ Queue_up, }; +#define BLK_TAGS_PER_LONG (sizeof(unsigned long) * 8) +#define BLK_TAGS_MASK (BLK_TAGS_PER_LONG - 1) + +struct blk_queue_tag { +struct request **tag_index;/* map of busy tags */ +unsigned long *tag_map;/* bit map of free/busy tags */ +struct list_head busy_list;/* fifo list of busy tags */ +int busy;/* current depth */ +int max_depth; +}; +/* * Default nr free requests per queue, ll_rw_blk will scale it down * according to available RAM at init time
@@ -193,6 +207,8 @@unsigned long seg_boundary_mask;wait_queue_head_t queue_wait; + +struct blk_queue_tag *queue_tags; };#define RQ_INACTIVE (-1)
@@ -203,9 +219,11 @@#define QUEUE_FLAG_PLUGGED 0/* queue is plugged */ #define QUEUE_FLAG_CLUSTER 1/* cluster several segments into 1 */ +#define QUEUE_FLAG_QUEUED 2/* uses generic tag queueing */ #define blk_queue_plugged (q) test_bit(QUEUE_FLAG_PLUGGED, &(q)->queue_flags)#define blk_mark_plugged (q) set_bit(QUEUE_FLAG_PLUGGED, &(q)->queue_flags) +#define blk_queue_tagged (q) test_bit(QUEUE_FLAG_QUEUED, &(q)->queue_flags)#define blk_queue_empty (q) elv_queue_empty(q)#define list_entry_rq (ptr) list_entry((ptr), struct request, queuelist)
@@ -315,6 +333,19 @@extern int blk_rq_map_sg (request_queue_t *,struct request *,struct scatterlist *);extern void blk_dump_rq_flags (struct request *,char *);extern void generic_unplug_device (void *); + +/* +* tag stuff +*/ +#define blk_queue_tag_request (q, tag) ((q)->queue_tags->tag_index[(tag)]) +#define blk_queue_tag_depth (q) ((q)->queue_tags->busy) +#define blk_queue_tag_queue (q) ((q)->queue_tags->busy < (q)->queue_tags->max_depth) +#define blk_rq_tagged (rq) ((rq)->flags & REQ_QUEUED) +extern int blk_queue_start_tag (request_queue_t *,struct request *); +extern void blk_queue_end_tag (request_queue_t *,struct request *); +extern int blk_queue_init_tags (request_queue_t *,int ); +extern void blk_queue_free_tags (request_queue_t *); +extern void blk_queue_invalidate_tags (request_queue_t *);extern int * blk_size[MAX_BLKDEV ];/* in units of 1024 bytes */ extern int * blksize_size[MAX_BLKDEV ];
# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas:
# ChangeSet 1.570 -> 1.571
# drivers/ide/ide-probe.c 1.41 -> 1.42
# drivers/ide/ide-disk.c 1.44 -> 1.45
# drivers/ide/Makefile 1.16 -> 1.17
# drivers/ide/ide-dma.c 1.35 -> 1.36
# include/linux/ide.h 1.51 -> 1.52
# drivers/ide/ide.c 1.71 -> 1.72
# drivers/ide/ide-taskfile.c 1.30 -> 1.31
# drivers/ide/Config.help 1.17 -> 1.18
# drivers/ide/Config.in 1.20 -> 1.21
# drivers/ide/pdc202xx.c 1.24 -> 1.25
# (new) -> 1.1 drivers/ide/ide-tcq.c
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/05/01 [email protected] 1.571
# Initial ide tcq support, based on the new block helpers
# --------------------------------------------
#
drivers/ide/Config.help +29 -0
@@ -751,6 +751,35 @@ Generally say N here. +CONFIG_BLK_DEV_IDE_TCQ + Support for tagged command queueing on ATA disk drives. This enables + the IDE layer to have multiple in-flight requests on hardware that + supports it. For now this includes the IBM Deskstar series drives, + such as the 22GXP, 75GXP, 40GV, 60GXP, and 120GXP (ie any Deskstar made + in the last couple of years), and at least some of the Western + Digital drives in the Expert series. + + If you have such a drive, say Y here. + +CONFIG_BLK_DEV_IDE_TCQ_DEPTH + Maximum size of commands to enable per-drive. Any value between 1 + and 32 is valid, with 32 being the maxium that the hardware supports. + + You probably just want the default of 32 here. If you enter an invalid + number, the default value will be used. + +CONFIG_BLK_DEV_IDE_TCQ_DEFAULT + Enabled tagged command queueing unconditionally on drives that report + support for it. Regardless of the chosen value here, tagging can be + controlled at run time: + + echo "using_tcq:32" > /proc/ide/hdX/settings + + where any value between 1-32 selects chosen queue depth and enables + TCQ, and 0 disables it. + + Generally say Y here. + CONFIG_BLK_DEV_IT8172 Say Y here to support the on-board IDE controller on the Integrated Technology Express, Inc. ITE8172 SBC. Vendor page at
drivers/ide/Config.in +5 -0
@@ -47,6 +47,11 @@ dep_bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO $CONFIG_BLK_DEV_IDEDMA_PCI dep_bool ' Enable DMA only for disks ' CONFIG_IDEDMA_ONLYDISK $CONFIG_IDEDMA_PCI_AUTO define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' ATA tagged command queueing' CONFIG_BLK_DEV_IDE_TCQ $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' TCQ on by default' CONFIG_BLK_DEV_IDE_TCQ_DEFAULT $CONFIG_BLK_DEV_IDE_TCQ + if [ "$CONFIG_BLK_DEV_IDE_TCQ" != "n" ]; then + int ' Default queue depth' CONFIG_BLK_DEV_IDE_TCQ_DEPTH 32 + fi dep_bool ' Good-Bad DMA Model-Firmware (EXPERIMENTAL)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_EXPERIMENTAL dep_bool ' AEC62XX chipset support' CONFIG_BLK_DEV_AEC62XX $CONFIG_BLK_DEV_IDEDMA_PCI dep_mbool ' AEC62XX Tuning support' CONFIG_AEC62XX_TUNING $CONFIG_BLK_DEV_AEC62XX
drivers/ide/Makefile +1 -0
@@ -44,6 +44,7 @@ ide-obj-$(CONFIG_BLK_DEV_HT6560B) += ht6560b.o ide-obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o ide-obj-$(CONFIG_BLK_DEV_IDEDMA_PCI) += ide-dma.o +ide-obj-$(CONFIG_BLK_DEV_IDE_TCQ) += ide-tcq.o ide-obj-$(CONFIG_BLK_DEV_IDEPCI) += ide-pci.o ide-obj-$(CONFIG_BLK_DEV_ISAPNP) += ide-pnp.o ide-obj-$(CONFIG_BLK_DEV_IDE_PMAC) += ide-pmac.o
drivers/ide/ide-disk.c +130 -4
205 lines not on this page · raw message
drivers/ide/ide-dma.c +46 -12
92 lines not on this page · raw message
drivers/ide/ide-probe.c +10 -0
@@ -198,6 +198,16 @@if (drive->channel->quirkproc) drive->quirk_list = drive->channel->quirkproc (drive); +/* Initialize queue depth settings */ + drive->queue_depth =1 ; +#ifdef CONFIG_BLK_DEV_IDE_TCQ_DEPTH + drive->queue_depth =CONFIG_BLK_DEV_IDE_TCQ_DEPTH ; +#else + drive->queue_depth = drive->id->queue_depth +1 ; +#endif +if (drive->queue_depth <1 || drive->queue_depth >IDE_MAX_TAG ) + drive->queue_depth =IDE_MAX_TAG ; +return ; err_misc:
drivers/ide/ide-taskfile.c +34 -6
@@ -456,11 +456,39 @@if (args->prehandler !=NULL )return args->prehandler (drive, rq); }else { -/* for dma commands we down set the handler */ -if (drive->using_dma && - !(drive->channel->udma (((args->taskfile.command ==WIN_WRITEDMA ) - || (args->taskfile.command ==WIN_WRITEDMA_EXT )) - ? ide_dma_write : ide_dma_read, drive, rq))); +ide_dma_action_t dma_act; +int tcq =0 ; + +if (!drive->using_dma) +return ide_started; + +/* for dma commands we don't set the handler */ +if (args->taskfile.command ==WIN_WRITEDMA || args->taskfile.command ==WIN_WRITEDMA_EXT ) + dma_act = ide_dma_write; +else if (args->taskfile.command ==WIN_READDMA || args->taskfile.command ==WIN_READDMA_EXT ) + dma_act = ide_dma_read; +else if (args->taskfile.command ==WIN_WRITEDMA_QUEUED || args->taskfile.command ==WIN_WRITEDMA_QUEUED_EXT ) { + tcq =1 ; + dma_act = ide_dma_write_queued; + }else if (args->taskfile.command ==WIN_READDMA_QUEUED || args->taskfile.command ==WIN_READDMA_QUEUED_EXT ) { + tcq =1 ; + dma_act = ide_dma_read_queued; + }else { +printk ("ata_taskfile: unknown command %x\n" , args->taskfile.command); +return ide_stopped; + } + +/* +* FIXME: this is a gross hack, need to unify tcq dma proc and +* regular dma proc -- basically split stuff that needs to act +* on a request from things like ide_dma_check etc. +*/ +if (tcq) +return drive->channel->udma (dma_act, drive, rq); +else { +if (drive->channel->udma (dma_act, drive, rq)) +return ide_stopped; + } }return ide_started;
@@ -523,7 +551,7 @@ide__sti ();/* local CPU only */ if (!OK_STAT (stat =GET_STAT (),READY_STAT ,BAD_STAT )) { -/* Keep quite for NOP becouse they are expected to fail. */ +/* Keep quiet for NOP because it is expected to fail. */ if (args && args->taskfile.command !=WIN_NOP )return ide_error (drive,"task_no_data_intr" , stat); }
drivers/ide/ide-tcq.c new file +600 -0
600 lines not on this page · raw message
drivers/ide/ide.c +10 -8
@@ -397,7 +397,10 @@if (!end_that_request_first (rq, uptodate, nr_secs)) {add_blkdev_randomness (major (rq->rq_dev)); -blkdev_dequeue_request (rq); +if (!blk_rq_tagged (rq)) +blkdev_dequeue_request (rq); +else +blk_queue_end_tag (&drive->queue, rq);HWGROUP (drive)->rq =NULL ;end_that_request_last (rq); ret =0 ;
@@ -1306,11 +1309,6 @@ } -/* Place holders for later expansion of functionality. -*/ -#define ata_pending_commands (drive) (0) -#define ata_can_queue (drive) (1) -/* * Feed commands to a drive until it barfs. Called with ide_lock/DRIVE_LOCK * held and busy channel.
@@ -1351,7 +1349,7 @@* still a severe BUG! */ if (blk_queue_plugged (&drive->queue)) { -BUG (); +BUG_ON (!drive->using_tcq);break ; }
@@ -1763,7 +1761,8 @@ }else {printk ("%s: %s: huh? expected NULL handler on exit\n" , drive->name, __FUNCTION__); } - } + }else if (startstop == ide_released) +queue_commands (drive, ch->irq); out_lock:spin_unlock_irqrestore (&ide_lock, flags);
@@ -3306,6 +3305,9 @@ drive->channel->udma (ide_dma_off_quietly, drive,NULL ); drive->channel->udma (ide_dma_check, drive,NULL ); +#ifdef CONFIG_BLK_DEV_IDE_TCQ_DEFAULT + drive->channel->udma (ide_dma_queued_on, drive,NULL ); +#endif }/* Only CD-ROMs and tape drives support DSC overlap. But only
drivers/ide/pdc202xx.c +6 -0
@@ -1057,6 +1057,12 @@case ide_dma_timeout:if (drive->channel->resetproc !=NULL ) drive->channel->resetproc (drive); +/* +* we cannot support queued operations on promise +*/ +case ide_dma_queued_on: +return 1 ; +default :break ; }
include/linux/ide.h +39 -1
@@ -300,6 +300,7 @@u8 tune_req;/* requested drive tuning setting */ byte using_dma;/* disk is using dma for read/write */ +byte using_tcq;/* disk is using queueing */ byte retry_pio;/* retrying dma capable host in pio */ byte state;/* retry state */ byte dsc_overlap;/* flag: DSC overlap */
@@ -362,9 +363,17 @@byte dn;/* now wide spread use */ byte wcache;/* status of write cache */ byte acoustic;/* acoustic management */ +byte queue_depth;/* max queue depth */ unsigned int failures;/* current failure count */ unsigned int max_failures;/* maximum allowed failure count */ struct device device;/* global device tree handle */ +/* +* tcq statistics +*/ +unsigned long immed_rel; +unsigned long immed_comp; +int max_last_depth; +int max_depth; } ide_drive_t;/*
@@ -383,7 +392,10 @@ ide_dma_off, ide_dma_off_quietly, ide_dma_test_irq, ide_dma_bad_drive, ide_dma_good_drive, ide_dma_verbose, ide_dma_retune, - ide_dma_lostirq, ide_dma_timeout + ide_dma_lostirq, ide_dma_timeout, + ide_dma_read_queued, ide_dma_write_queued, + ide_dma_queued_start, ide_dma_queued_on, + ide_dma_queued_off, } ide_dma_action_t; enum {
@@ -464,6 +476,7 @@ unsigned highmem : 1; /* can do full 32-bit dma */ unsigned no_io_32bit : 1; /* disallow enabling 32bit I/O */ unsigned no_unmask : 1; /* disallow setting unmask bit */ + unsigned auto_poll : 1; /* supports nop auto-poll */byte io_32bit;/* 0=16-bit, 1=32-bit, 2/3=32bit+sync */ byte unmask;/* flag: okay to unmask other irqs */ byte slow;/* flag: slow data port */
@@ -503,6 +516,29 @@#define IDE_SLEEP 1#define IDE_DMA 2/* DMA in progress */ +#define IDE_MAX_TAG 32 + +#ifdef CONFIG_BLK_DEV_IDE_TCQ +static inline int ata_pending_commands (ide_drive_t *drive) +{ +if (drive->using_tcq) +return blk_queue_tag_depth (&drive->queue); + +return 0 ; +} + +static inline int ata_can_queue (ide_drive_t *drive) +{ +if (drive->using_tcq) +return blk_queue_tag_queue (&drive->queue); + +return 1 ; +} +#else +#define ata_pending_commands (drive) (0) +#define ata_can_queue (drive) (1) +#endif + typedef struct hwgroup_s { ide_startstop_t (*handler)(struct ata_device *, struct request *); /* irq handler, if active */ unsigned long flags; /* BUSY, SLEEPING */
@@ -861,9 +897,11 @@extern ide_startstop_t ide_dma_intr (struct ata_device *,struct request *);int check_drive_lists (ide_drive_t *drive,int good_bad);int ide_dmaproc (ide_dma_action_t func,struct ata_device *drive,struct request *); +ide_startstop_t ide_tcq_dmaproc (ide_dma_action_t func,struct ata_device *drive,struct request *);extern void ide_release_dma (struct ata_channel *hwif);extern void ide_setup_dma (struct ata_channel *hwif,unsigned long dmabase,unsigned int num_ports)__init ; +extern int ide_start_dma (ide_dma_action_t func,struct ata_device *drive);#endif extern spinlock_t ide_lock;
Quoted 4 lines · Jens Axboe
I've rewritten parts of the IDE TCQ stuff to be, well, a lot better in my oppinion. I had to accept that the
ata_requestandrq->specialusage sucked, it was just one big mess.
Looks good.
I would have one more comment - it would be wonderful if somebody else who was using tagged commands were to try to use this interface too, just to verify that it doesn't have any major problems.
I'll be happy to merge the generic tag stuff even without that (on the assumption that whatever deficiencies can be fixed later anyway), but it would be wonderful to have some kind of validation of the genericity.
In particular, on an IDE TCQ device we expect the queue depth to be fixed, but the SCSI subsystem considers queue depth to be only approximate (because the drive internally might split a command or something, I don't know). So I wonder how well this interface would interact with the QUEUE_FULL handling that the SCSI layer does.
Maybe it doesn't matter, and maybe some blk_queue_invalidate_tags() magic could do the right thing, but I'd like to get some kind of idea of whether others can use it well..
(If not the SCSI layer, maybe somebody could look at DAC960 or cciss and see whether they would seem to like the tag interface).
Linus
Uz.ytkownik Linus Torvalds napisa?:
Quoted 9 lines · Jens Axboe
Quoted 4 lines
I've rewritten parts of the IDE TCQ stuff to be, well, a lot better in my oppinion. I had to accept that the
ata_requestandrq->specialusage sucked, it was just one big mess.Looks good.
Well after a short cross over look at it I agree. The generic interface looks sane for me as well. However I will have to look a bit deeper, becouse at the first sight the double pointer to tag_index looks a bit "overelaborate" to me. But I may change my opinnion after looking at the actual usage - so please take this small bit of critique with a good grain of salt...
+#define BLK_TAGS_PER_LONG (sizeof(unsigned long) * 8)
+#define BLK_TAGS_MASK (BLK_TAGS_PER_LONG - 1)
+
+struct blk_queue_tag {
+ struct request **tag_index; /* map of busy tags */
+ unsigned long *tag_map; /* bit map of free/busy tags */
+ struct list_head busy_list; /* fifo list of busy tags */
+ int busy; /* current depth */
+ int max_depth;
+};
+
Uz.ytkownik Martin Dalecki napisa?:
Quoted 23 lines
Uz.ytkownik Linus Torvalds napisa?:
Quoted 10 lines · Jens Axboe
Quoted 4 lines
I've rewritten parts of the IDE TCQ stuff to be, well, a lot better in my oppinion. I had to accept that the
ata_requestandrq->specialusage sucked, it was just one big mess.Looks good.
Well after a short cross over look at it I agree. The generic interface looks sane for me as well. However I will have to look a bit deeper, becouse at the first sight the double pointer to
tag_indexlooks a bit "overelaborate" to me. But I may change my opinnion after looking at the actual usage - so please take this small bit of critique with a good grain of salt...include/linux/blkdev.h +1
-0+#define BLK_TAGS_PER_LONG (sizeof(unsigned long) * 8)
include/linux/blkdev.h line 137
Quoted 10 lines
include/linux/blkdev.h +10
-0+#define BLK_TAGS_MASK (BLK_TAGS_PER_LONG - 1) + +struct blk_queue_tag { +struct request **tag_index;/* map of busy tags */ +unsigned long *tag_map;/* bit map of free/busy tags */ +struct list_head busy_list;/* fifo list of busy tags */ +int busy;/* current depth */ +int max_depth; +}; +
Well I revoke my objections. tag_index is fine :-).
Quoted 8 lines · Martin Dalecki
Quoted 8 lines
Well after a short cross over look at it I agree. The generic interface looks sane for me as well. However I will have to look a bit deeper, becouse at the first sight the double pointer to
tag_indexlooks a bit "overelaborate" to me. But I may change my opinnion after looking at the actual usage - so please take this small bit of critique with a good grain of salt...
Quoted 13 lines
include/linux/blkdev.h line 137
Quoted 11 lines
include/linux/blkdev.h +11
-0+#define BLK_TAGS_PER_LONG (sizeof(unsigned long) * 8) +#define BLK_TAGS_MASK (BLK_TAGS_PER_LONG - 1) + +struct blk_queue_tag { +struct request **tag_index;/* map of busy tags */ +unsigned long *tag_map;/* bit map of free/busy tags */ +struct list_head busy_list;/* fifo list of busy tags */ +int busy;/* current depth */ +int max_depth; +}; +Well I revoke my objections.
tag_indexis fine :-).
It should be, it's just an index of pointer to request, so no double indirections :)
Signature · 1 line
Jens Axboe
Quoted 10 lines · Użytkownik Jens Axboe
Hi,
I've rewritten parts of the IDE TCQ stuff to be, well, a lot better in my oppinion. I had to accept that the
ata_requestandrq->specialusage sucked, it was just one big mess.So following a suggestion from Martin and Linus, I implemented some basic tagged command queueing back end in the block layer. This is what the new IDE TCQ core is build on, and what potentially others can use as well. I'll start by describing the new API:
Looking at the IDE part we can now see that pushing the generic functions one level up the impact on the code flow on the IDE side is now:
1. Low (most of stuff is due to the ugly /proc special-ide-interface.
2. Nicely isolated.
Great work Jens! (just my humble opinnion).
However I see a note about the need
to unify the DMA parts, so I will se what can be done on this side becouse I have always planned to get rid of the silly switch(ide_dma_function_t) on the dmaproc-path.
May I ask you as well to just call ide-tcq.c simple tcq.c? The ide- is entierly redundant and I see no need to stick to the previous "convention" here. It is just a leftover from the days where the IDE stuff didn't sit in his own directory. In general I rather prefer the prefix ata_ instead of ide_ becouse we are on the command level and on the host here -
ide resides on the disk and the whole world
outside linux calls it ata_. Finally ata_ is far better grep-able overall becouse the ide letter combination is very common :-) But that's a minor nit of course.
My convention is to prefix functions with the module specific prefix only if they are exported, which means:
1. They are directly external.
2. They are hiddenly exported by setting some methods in structs to them.
Otherwise I stick to the most convenient semantically related name without the fear that it could sound too generic... so queue_data() is meant to be local for example and it doesn't clash with the generic bio_queue_data() or whatever.
For me this convention turned out to help narrowing the focus during reading code...
Quoted 24 lines · Linus Torvalds
Quoted 4 lines · Jens Axboe
I've rewritten parts of the IDE TCQ stuff to be, well, a lot better in my oppinion. I had to accept that the
ata_requestandrq->specialusage sucked, it was just one big mess.Looks good.
I would have one more comment - it would be wonderful if somebody else who was using tagged commands were to try to use this interface too, just to verify that it doesn't have any major problems.
I'll be happy to merge the generic tag stuff even without that (on the assumption that whatever deficiencies can be fixed later anyway), but it would be wonderful to have some kind of validation of the genericity.
In particular, on an IDE TCQ device we expect the queue depth to be fixed, but the SCSI subsystem considers queue depth to be only approximate (because the drive internally might split a command or something, I don't know). So I wonder how well this interface would interact with the
QUEUE_FULLhandling that the SCSI layer does.
Quoted 6 lines
Maybe it doesn't matter, and maybe some
blk_queue_invalidate_tags()magic could do the right thing, but I'd like to get some kind of idea of whether others can use it well..(If not the SCSI layer, maybe somebody could look at DAC960 or cciss and see whether they would seem to like the tag interface).
Yeah, that's why I said the interface was simple on purpose right now. I'm sure it's extendable for some sort of adaptive queueing as well, I will try to provide some examples for that. cciss will eat a lot more commands than you think, it typically has a bigger internal queue depth than the block layer :-). If not dac960, then SCSI is a possibility.
Signature · 1 line
Jens Axboe
Quoted 17 lines · Martin Dalecki
U?ytkownik Jens Axboe napisa?:
Quoted 10 lines
Hi,
I've rewritten parts of the IDE TCQ stuff to be, well, a lot better in my oppinion. I had to accept that the
ata_requestandrq->specialusage sucked, it was just one big mess.So following a suggestion from Martin and Linus, I implemented some basic tagged command queueing back end in the block layer. This is what the new IDE TCQ core is build on, and what potentially others can use as well. I'll start by describing the new API:
Looking at the IDE part we can now see that pushing the generic functions one level up the impact on the code flow on the IDE side is now:
Quoted 5 lines
1. Low (most of stuff is due to the ugly /proc special-ide-interface.
2. Nicely isolated.
Great work Jens! (just my humble opinnion).
Thanks!
Quoted 4 lines
However I see a note about the need
to unify the DMA parts, so I will se what can be done on this side becouse I have always planned to get rid of the sillyswitch(ide_dma_function_t)on the dmaproc-path.
I'll try and get some work done on that tomorrow, right now the dmaproc crap is a garbage bin of stuff rolled into one.
Quoted 10 lines
May I ask you as well to just call
ide-tcq.csimpletcq.c? The ide- is entierly redundant and I see no need to stick to the previous "convention" here. It is just a leftover from the days where the IDE stuff didn't sit in his own directory. In general I rather prefer the prefixata_instead ofide_becouse we are on the command level and on the host here -
ide resides on the disk and the whole world
outside linux calls itata_. Finallyata_is far better grep-able overall becouse the ide letter combination is very common :-) But that's a minor nit of course.
I agree on the ata prefixing, the ide-* has always annoyed me as well. And you can rename it to tcq.c if you want, I have no oppinion on that.
Signature · 1 line
Jens Axboe