Commit 0c686811 authored by Jens Axboe's avatar Jens Axboe Committed by Linus Torvalds

[PATCH] 2.5.8 IDE 35

- Expand configure help options a bit
- Fix xconfig bug
- Decrease queue depth if a command takes too long to complete
- Test master/slave stuff. It works, but one device can heavily starve
   another. This is the simple approach right now, means that one device
   will wait until the other is completely idle before starting any
   commands This is not necessary since we can have queued commands on
   both devices at the same time. TODO.
- Add proc output for oldest command, just for testing.
- pci_dev compile fixes.
- Make sure ide-disk doesn't BUG if TCQ is not used, basically this was
   fixed by off-loading the using_tcq setting to ide-tcq.
- Remove warning about 'queued feature set not supported'
- Abstract ide_tcq_wait_dataphase() into a function
parent 21aecbc3
...@@ -753,9 +753,17 @@ CONFIG_BLK_DEV_IDE_TCQ ...@@ -753,9 +753,17 @@ CONFIG_BLK_DEV_IDE_TCQ
If you have such a drive, say Y here. If you have such a drive, say Y here.
CONFIG_BLK_DEV_IDE_TCQ_DEFAULT CONFIG_BLK_DEV_IDE_TCQ_FULL
Enabled tagged command queueing unconditionally on drives that report When a command completes from the drive, the SERVICE bit is checked to
support for it. see if other queued commands are ready to be started. Doing this
immediately after a command completes has a tendency to 'starve' the
device hardware queue, since we risk emptying the queue completely
before starting any new commands. This shows up during stressing the
drive as a /\/\/\/\ queue size balance, where we could instead try and
maintain a minimum queue size and get a /---------\ graph instead.
Saying Y here will attempt to always keep the queue full when possible
at the cost of possibly increasing command turn-around latency.
Generally say Y here. Generally say Y here.
...@@ -766,6 +774,18 @@ CONFIG_BLK_DEV_IDE_TCQ_DEPTH ...@@ -766,6 +774,18 @@ CONFIG_BLK_DEV_IDE_TCQ_DEPTH
You probably just want the default of 32 here. If you enter an invalid You probably just want the default of 32 here. If you enter an invalid
number, the default value will be used. 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 CONFIG_BLK_DEV_IT8172
Say Y here to support the on-board IDE controller on the Integrated Say Y here to support the on-board IDE controller on the Integrated
Technology Express, Inc. ITE8172 SBC. Vendor page at Technology Express, Inc. ITE8172 SBC. Vendor page at
......
...@@ -48,10 +48,11 @@ if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then ...@@ -48,10 +48,11 @@ if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
dep_bool ' Enable DMA only for disks ' CONFIG_IDEDMA_ONLYDISK $CONFIG_IDEDMA_PCI_AUTO 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 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 ' 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 dep_bool ' Attempt to keep queue full' CONFIG_BLK_DEV_IDE_TCQ_FULL $CONFIG_BLK_DEV_IDE_TCQ
if [ $CONFIG_BLK_DEV_IDE_TCQ_DEFAULT != "n" ]; then dep_bool ' TCQ on by default' CONFIG_BLK_DEV_IDE_TCQ_DEFAULT $CONFIG_BLK_DEV_IDE_TCQ
int ' Default queue depth' CONFIG_BLK_DEV_IDE_TCQ_DEPTH 32 if [ "$CONFIG_BLK_DEV_IDE_TCQ" != "n" ]; then
fi int ' Default queue depth' CONFIG_BLK_DEV_IDE_TCQ_DEPTH 8
fi
dep_bool ' ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL dep_bool ' ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL
dep_bool ' Good-Bad DMA Model-Firmware (WIP)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_IDEDMA_PCI_WIP dep_bool ' Good-Bad DMA Model-Firmware (WIP)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_IDEDMA_PCI_WIP
dep_bool ' AEC62XX chipset support' CONFIG_BLK_DEV_AEC62XX $CONFIG_BLK_DEV_IDEDMA_PCI dep_bool ' AEC62XX chipset support' CONFIG_BLK_DEV_AEC62XX $CONFIG_BLK_DEV_IDEDMA_PCI
......
...@@ -107,10 +107,7 @@ static u8 get_command(ide_drive_t *drive, int cmd) ...@@ -107,10 +107,7 @@ static u8 get_command(ide_drive_t *drive, int cmd)
* 48-bit commands are pretty sanely laid out * 48-bit commands are pretty sanely laid out
*/ */
if (lba48bit) { if (lba48bit) {
if (cmd == READ) command = cmd == READ ? WIN_READ_EXT : WIN_WRITE_EXT;
command = WIN_READ_EXT;
else
command = WIN_WRITE_EXT;
if (drive->using_dma) { if (drive->using_dma) {
command++; /* WIN_*DMA_EXT */ command++; /* WIN_*DMA_EXT */
...@@ -118,31 +115,33 @@ static u8 get_command(ide_drive_t *drive, int cmd) ...@@ -118,31 +115,33 @@ static u8 get_command(ide_drive_t *drive, int cmd)
command++; /* WIN_*DMA_QUEUED_EXT */ command++; /* WIN_*DMA_QUEUED_EXT */
} else if (drive->mult_count) } else if (drive->mult_count)
command += 5; /* WIN_MULT*_EXT */ command += 5; /* WIN_MULT*_EXT */
} else {
/* return command;
* 28-bit commands seem not to be, though... }
*/
if (cmd == READ) { /*
if (drive->using_dma) { * 28-bit commands seem not to be, though...
if (drive->using_tcq) */
command = WIN_READDMA_QUEUED; if (cmd == READ) {
else if (drive->using_dma) {
command = WIN_READDMA; if (drive->using_tcq)
} else if (drive->mult_count) command = WIN_READDMA_QUEUED;
command = WIN_MULTREAD;
else else
command = WIN_READ; command = WIN_READDMA;
} else { } else if (drive->mult_count)
if (drive->using_dma) { command = WIN_MULTREAD;
if (drive->using_tcq) else
command = WIN_WRITEDMA_QUEUED; command = WIN_READ;
else } else {
command = WIN_WRITEDMA; if (drive->using_dma) {
} else if (drive->mult_count) if (drive->using_tcq)
command = WIN_MULTWRITE; command = WIN_WRITEDMA_QUEUED;
else else
command = WIN_WRITE; command = WIN_WRITEDMA;
} } else if (drive->mult_count)
command = WIN_MULTWRITE;
else
command = WIN_WRITE;
} }
return command; return command;
...@@ -895,24 +894,24 @@ static int proc_idedisk_read_tcq ...@@ -895,24 +894,24 @@ static int proc_idedisk_read_tcq
__set_bit(i, &tag_mask); __set_bit(i, &tag_mask);
len += sprintf(out+len, "%d, ", i); len += sprintf(out+len, "%d, ", i);
if (ar->ar_time > max_jif) if (cur_jif - ar->ar_time > max_jif)
max_jif = ar->ar_time; max_jif = cur_jif - ar->ar_time;
cmds++; cmds++;
} }
len += sprintf(out+len, "]\n"); len += sprintf(out+len, "]\n");
len += sprintf(out+len, "Queue:\t\t\treleased [ %d ] - started [ %d ]\n", drive->tcq->immed_rel, drive->tcq->immed_comp);
if (drive->tcq->queued != cmds) if (drive->tcq->queued != cmds)
len += sprintf(out+len, "pending request and queue count mismatch (%d)\n", cmds); len += sprintf(out+len, "pending request and queue count mismatch (counted: %d)\n", cmds);
if (tag_mask != drive->tcq->tag_mask) if (tag_mask != drive->tcq->tag_mask)
len += sprintf(out+len, "tag masks differ (counted %lx != %lx\n", tag_mask, drive->tcq->tag_mask); len += sprintf(out+len, "tag masks differ (counted %lx != %lx\n", tag_mask, drive->tcq->tag_mask);
len += sprintf(out+len, "DMA status:\t\t%srunning\n", test_bit(IDE_DMA, &HWGROUP(drive)->flags) ? "" : "not "); len += sprintf(out+len, "DMA status:\t\t%srunning\n", test_bit(IDE_DMA, &HWGROUP(drive)->flags) ? "" : "not ");
if (max_jif) len += sprintf(out+len, "Oldest command:\t\t%lu jiffies\n", max_jif);
len += sprintf(out+len, "Oldest command:\t\t%lu\n", cur_jif - max_jif); len += sprintf(out+len, "Oldest command ever:\t%lu\n", drive->tcq->oldest_command);
len += sprintf(out+len, "immed rel %d, immed comp %d\n", drive->tcq->immed_rel, drive->tcq->immed_comp);
drive->tcq->max_last_depth = 0; drive->tcq->max_last_depth = 0;
...@@ -1017,8 +1016,10 @@ static int set_using_tcq(ide_drive_t *drive, int arg) ...@@ -1017,8 +1016,10 @@ static int set_using_tcq(ide_drive_t *drive, int arg)
return -EPERM; return -EPERM;
if (!drive->channel->dmaproc) if (!drive->channel->dmaproc)
return -EPERM; return -EPERM;
if (arg == drive->queue_depth && drive->using_tcq)
return 0;
drive->using_tcq = arg; drive->queue_depth = arg ? arg : 1;
if (drive->channel->dmaproc(arg ? ide_dma_queued_on : ide_dma_queued_off, drive)) if (drive->channel->dmaproc(arg ? ide_dma_queued_on : ide_dma_queued_off, drive))
return -EIO; return -EIO;
......
...@@ -610,9 +610,6 @@ int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive) ...@@ -610,9 +610,6 @@ int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
case ide_dma_check: case ide_dma_check:
return config_drive_for_dma (drive); return config_drive_for_dma (drive);
case ide_dma_begin: case ide_dma_begin:
#ifdef DEBUG
printk("ide_dma_begin: from %p\n", __builtin_return_address(0));
#endif
if (test_and_set_bit(IDE_DMA, &HWGROUP(drive)->flags)) if (test_and_set_bit(IDE_DMA, &HWGROUP(drive)->flags))
BUG(); BUG();
/* Note that this is done *after* the cmd has /* Note that this is done *after* the cmd has
...@@ -634,14 +631,13 @@ int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive) ...@@ -634,14 +631,13 @@ int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
case ide_dma_read: case ide_dma_read:
reading = 1 << 3; reading = 1 << 3;
case ide_dma_write: case ide_dma_write:
ar = HWGROUP(drive)->rq->special; ar = IDE_CUR_AR(drive);
if (ide_start_dma(hwif, drive, func)) if (ide_start_dma(hwif, drive, func))
return 1; return 1;
if (drive->type != ATA_DISK) if (drive->type != ATA_DISK)
return 0; return 0;
BUG_ON(HWGROUP(drive)->handler); BUG_ON(HWGROUP(drive)->handler);
ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, dma_timer_expiry); /* issue cmd to drive */ ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, dma_timer_expiry); /* issue cmd to drive */
if ((ar->ar_rq->flags & REQ_DRIVE_TASKFILE) && if ((ar->ar_rq->flags & REQ_DRIVE_TASKFILE) &&
...@@ -655,20 +651,13 @@ int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive) ...@@ -655,20 +651,13 @@ int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
} }
return hwif->dmaproc(ide_dma_begin, drive); return hwif->dmaproc(ide_dma_begin, drive);
case ide_dma_end: /* returns 1 on error, 0 otherwise */ case ide_dma_end: /* returns 1 on error, 0 otherwise */
#ifdef DEBUG if (!test_and_clear_bit(IDE_DMA, &HWGROUP(drive)->flags))
printk("ide_dma_end: from %p\n", __builtin_return_address(0)); BUG();
#endif
if (!test_and_clear_bit(IDE_DMA, &HWGROUP(drive)->flags)) {
printk("ide_dma_end: dma not going? %p\n", __builtin_return_address(0));
return 1;
}
drive->waiting_for_dma = 0; drive->waiting_for_dma = 0;
outb(inb(dma_base)&~1, dma_base); /* stop DMA */ outb(inb(dma_base)&~1, dma_base); /* stop DMA */
dma_stat = inb(dma_base+2); /* get DMA status */ dma_stat = inb(dma_base+2); /* get DMA status */
outb(dma_stat|6, dma_base+2); /* clear the INTR & ERROR bits */ outb(dma_stat|6, dma_base+2); /* clear the INTR & ERROR bits */
ide_destroy_dmatable(drive); /* purge DMA mappings */ ide_destroy_dmatable(drive); /* purge DMA mappings */
if (drive->tcq)
IDE_SET_CUR_TAG(drive, -1);
return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0; /* verify good DMA status */ return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0; /* verify good DMA status */
case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */ case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
dma_stat = inb(dma_base+2); dma_stat = inb(dma_base+2);
......
...@@ -192,19 +192,16 @@ static inline void do_identify (ide_drive_t *drive, byte cmd) ...@@ -192,19 +192,16 @@ static inline void do_identify (ide_drive_t *drive, byte cmd)
/* /*
* it's an ata drive, build command list * it's an ata drive, build command list
*/ */
#ifndef CONFIG_BLK_DEV_IDE_TCQ
drive->queue_depth = 1; drive->queue_depth = 1;
#ifdef CONFIG_BLK_DEV_IDE_TCQ_DEPTH
drive->queue_depth = CONFIG_BLK_DEV_IDE_TCQ_DEPTH;
#else #else
# ifndef CONFIG_BLK_DEV_IDE_TCQ_DEPTH
# define CONFIG_BLK_DEV_IDE_TCQ_DEPTH 1
# endif
drive->queue_depth = drive->id->queue_depth + 1; drive->queue_depth = drive->id->queue_depth + 1;
if (drive->queue_depth > CONFIG_BLK_DEV_IDE_TCQ_DEPTH) #endif
drive->queue_depth = CONFIG_BLK_DEV_IDE_TCQ_DEPTH;
if (drive->queue_depth < 1 || drive->queue_depth > IDE_MAX_TAG) if (drive->queue_depth < 1 || drive->queue_depth > IDE_MAX_TAG)
drive->queue_depth = IDE_MAX_TAG; drive->queue_depth = IDE_MAX_TAG;
#endif
if (ide_build_commandlist(drive)) if (ide_init_commandlist(drive))
goto err_misc; goto err_misc;
return; return;
......
This diff is collapsed.
This diff is collapsed.
...@@ -648,6 +648,12 @@ ide_startstop_t promise_rw_disk (ide_drive_t *drive, struct request *rq, unsigne ...@@ -648,6 +648,12 @@ ide_startstop_t promise_rw_disk (ide_drive_t *drive, struct request *rq, unsigne
memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr));
/* The four drives on the two logical (one physical) interfaces
are distinguished by writing the drive number (0-3) to the
Feature register.
FIXME: Is promise_selectproc now redundant??
*/
taskfile.feature = (drive->channel->unit << 1) + drive->select.b.unit;
taskfile.sector_count = rq->nr_sectors; taskfile.sector_count = rq->nr_sectors;
taskfile.sector_number = block; taskfile.sector_number = block;
taskfile.low_cylinder = (block>>=8); taskfile.low_cylinder = (block>>=8);
......
...@@ -273,19 +273,22 @@ typedef union { ...@@ -273,19 +273,22 @@ typedef union {
} b; } b;
} special_t; } special_t;
#define IDE_MAX_TAG 32 /* spec says 32 max */ #define IDE_MAX_TAG (32) /* spec says 32 max */
#define IDE_INACTIVE_TAG (-1)
struct ata_request; struct ata_request;
typedef struct ide_tag_info_s { typedef struct ide_tag_info_s {
unsigned long tag_mask; /* next tag bit mask */ unsigned long tag_mask; /* next tag bit mask */
struct ata_request *ar[IDE_MAX_TAG]; /* in-progress requests */ struct ata_request *ar[IDE_MAX_TAG]; /* in-progress requests */
int active_tag; /* current active tag */ int active_tag; /* current active tag */
int queued; /* current depth */ int queued; /* current depth */
/* /*
* stats -> * stats ->
*/ */
int max_depth; /* max depth ever */ int max_depth; /* max depth ever */
int max_last_depth; /* max since last check */ int max_last_depth; /* max since last check */
/* /*
...@@ -295,14 +298,19 @@ typedef struct ide_tag_info_s { ...@@ -295,14 +298,19 @@ typedef struct ide_tag_info_s {
*/ */
int immed_rel; int immed_rel;
int immed_comp; int immed_comp;
unsigned long oldest_command;
} ide_tag_info_t; } ide_tag_info_t;
#define IDE_GET_AR(drive, tag) ((drive)->tcq->ar[(tag)]) #define IDE_GET_AR(drive, tag) ((drive)->tcq->ar[(tag)])
#define IDE_CUR_TAG(drive) (IDE_GET_AR((drive), (drive)->tcq->active_tag)) #define IDE_CUR_TAG(drive) (IDE_GET_AR((drive), (drive)->tcq->active_tag))
#define IDE_SET_CUR_TAG(drive, tag) ((drive)->tcq->active_tag = (tag)) #define IDE_SET_CUR_TAG(drive, tag) \
do { \
((drive)->tcq->active_tag = (tag)); \
if ((tag) == IDE_INACTIVE_TAG) \
HWGROUP((drive))->rq = NULL; \
} while (0);
#define IDE_CUR_AR(drive) \ #define IDE_CUR_AR(drive) (HWGROUP((drive))->rq->special)
((drive)->using_tcq ? IDE_CUR_TAG((drive)) : HWGROUP((drive))->rq->special)
struct ide_settings_s; struct ide_settings_s;
...@@ -359,6 +367,7 @@ typedef struct ide_drive_s { ...@@ -359,6 +367,7 @@ typedef struct ide_drive_s {
unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */ unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */
unsigned remap_0_to_1 : 2; /* 0=remap if ezdrive, 1=remap, 2=noremap */ unsigned remap_0_to_1 : 2; /* 0=remap if ezdrive, 1=remap, 2=noremap */
unsigned ata_flash : 1; /* 1=present, 0=default */ unsigned ata_flash : 1; /* 1=present, 0=default */
unsigned service_pending: 1;
unsigned addressing; /* : 2; 0=28-bit, 1=48-bit, 2=64-bit */ unsigned addressing; /* : 2; 0=28-bit, 1=48-bit, 2=64-bit */
byte scsi; /* 0=default, 1=skip current ide-subdriver for ide-scsi emulation */ byte scsi; /* 0=default, 1=skip current ide-subdriver for ide-scsi emulation */
select_t select; /* basic drive/head select reg value */ select_t select; /* basic drive/head select reg value */
...@@ -546,7 +555,7 @@ extern void ide_unregister(struct ata_channel *hwif); ...@@ -546,7 +555,7 @@ extern void ide_unregister(struct ata_channel *hwif);
typedef enum { typedef enum {
ide_stopped, /* no drive operation was started */ ide_stopped, /* no drive operation was started */
ide_started, /* a drive operation was started, and a handler was set */ ide_started, /* a drive operation was started, and a handler was set */
ide_released /* started and released bus */ ide_released, /* started, handler set, bus released */
} ide_startstop_t; } ide_startstop_t;
/* /*
...@@ -980,6 +989,11 @@ extern void revalidate_drives(void); ...@@ -980,6 +989,11 @@ extern void revalidate_drives(void);
#define ATA_AR_SETUP 2 #define ATA_AR_SETUP 2
#define ATA_AR_RETURN 4 #define ATA_AR_RETURN 4
/*
* if turn-around time is longer than this, halve queue depth
*/
#define ATA_AR_MAX_TURNAROUND (3 * HZ)
#define list_ata_entry(entry) list_entry((entry), struct ata_request, ar_queue) #define list_ata_entry(entry) list_entry((entry), struct ata_request, ar_queue)
static inline void ata_ar_init(ide_drive_t *drive, struct ata_request *ar) static inline void ata_ar_init(ide_drive_t *drive, struct ata_request *ar)
...@@ -1026,7 +1040,7 @@ static inline void ata_ar_put(ide_drive_t *drive, struct ata_request *ar) ...@@ -1026,7 +1040,7 @@ static inline void ata_ar_put(ide_drive_t *drive, struct ata_request *ar)
ar->ar_rq = NULL; ar->ar_rq = NULL;
} }
extern inline int ide_get_tag(ide_drive_t *drive) static inline int ide_get_tag(ide_drive_t *drive)
{ {
int tag = ffz(drive->tcq->tag_mask); int tag = ffz(drive->tcq->tag_mask);
...@@ -1043,12 +1057,28 @@ extern inline int ide_get_tag(ide_drive_t *drive) ...@@ -1043,12 +1057,28 @@ extern inline int ide_get_tag(ide_drive_t *drive)
} }
#ifdef CONFIG_BLK_DEV_IDE_TCQ #ifdef CONFIG_BLK_DEV_IDE_TCQ
# define ide_pending_commands(drive) ((drive)->using_tcq && (drive)->tcq->queued) static inline int ide_pending_commands(ide_drive_t *drive)
{
if (!drive->tcq)
return 0;
return drive->tcq->queued;
}
static inline int ide_can_queue(ide_drive_t *drive)
{
if (!drive->tcq)
return 1;
return drive->tcq->queued < drive->queue_depth;
}
#else #else
# define ide_pending_commands(drive) 0 #define ide_pending_commands(drive) (0)
#define ide_can_queue(drive) (1)
#endif #endif
int ide_build_commandlist(ide_drive_t *); int ide_build_commandlist(ide_drive_t *);
int ide_init_commandlist(ide_drive_t *);
void ide_teardown_commandlist(ide_drive_t *); void ide_teardown_commandlist(ide_drive_t *);
int ide_tcq_dmaproc(ide_dma_action_t, ide_drive_t *); int ide_tcq_dmaproc(ide_dma_action_t, ide_drive_t *);
ide_startstop_t ide_start_tag(ide_dma_action_t, ide_drive_t *, struct ata_request *); ide_startstop_t ide_start_tag(ide_dma_action_t, ide_drive_t *, struct ata_request *);
......
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