免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4628 | 回复: 2
打印 上一主题 下一主题

[转贴]分析IDE硬盘驱动器读写过程 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2003-02-09 13:06 |只看该作者 |倒序浏览
原文出自:http://www.linuxforum.net
作者:opera  
=================================================


Linux内核在缺省配置下最多支持10个IDE接口,IDE接口用ide_hwif_t结构来描述,每个IDE接口具有一对主-从驱动器接口,它们用ide_drive_t结构来描述,每个驱动器接口可接不同种类的IDE设备,如IDE硬盘,光驱等,它们用ide_driver_t结构来描述.
每个驱动器接口包含一个命令请求队列,用request_queue_t结构来描述,具体的请求用request结构来描述.

多个IDE驱动器可以共享一个中断,共享同一中断的驱动器形成一个组,用ide_hwgroup_t结构来描述.ide_intr()是所有的ide驱动器所共用的硬件中断入口,对之对应的ide_hwgroup_t指针将作为dev_id传递给ide_intr.

每次在读写某个驱动器之前,需要用ide_set_handler()来设置ide_intr将要调用的中断函数指针.中断产生以后,该函数指针被自动清除.

do_rw_disk(drive,rq,block) 从逻辑扇区号block开始向IDE硬盘驱动器drive写入rq所描述的内容.

以下是硬盘PIO传输模式的有关代码.
<code>;
; drivers/ide/ide-disk.c
static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
{
        if (IDE_CONTROL_REG)
                OUT_BYTE(drive->;ctl,IDE_CONTROL_REG);
        OUT_BYTE(rq->;nr_sectors,IDE_NSECTOR_REG);
        if (drive->;select.b.lba) { 如果是逻辑块寻址模式
                OUT_BYTE(block,IDE_SECTOR_REG);
                OUT_BYTE(block>;>;=8,IDE_LCYL_REG);
                OUT_BYTE(block>;>;=8,IDE_HCYL_REG);
                OUT_BYTE(((block>;>;&amp;0x0f)|drive->;select.all,IDE_SELECT_REG);
        } else {
                unsigned int sect,head,cyl,track;
                track = block / drive->;sect;
                sect  = block % drive->;sect + 1;
                OUT_BYTE(sect,IDE_SECTOR_REG);
                head  = track % drive->;head;
                cyl   = track / drive->;head;
                OUT_BYTE(cyl,IDE_LCYL_REG);
                OUT_BYTE(cyl>;>;8,IDE_HCYL_REG);
                OUT_BYTE(head|drive->;select.all,IDE_SELECT_REG);
        }
        if (rq->;cmd == READ) {{
                ide_set_handler(drive, &amp;read_intr, WAIT_CMD, NULL); WAIT_CMD为10秒超时
                OUT_BYTE(drive->;mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG);
                return ide_started;
        }
        if (rq->;cmd == WRITE) {
                ide_startstop_t startstop;
                OUT_BYTE(drive->;mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG);
                if (ide_wait_stat(&amp;startstop, drive, DATA_READY, drive->;bad_wstat, WAIT_DRQ)) {
                        printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->;name,
                                drive->;mult_count ? "MULTWRITE" : "WRITE";
                        return startstop;
                }
                if (!drive->;unmask)
                        __cli();        /* local CPU only */
                if (drive->;mult_count) { 如果允许多扇区传送
                        ide_hwgroup_t *hwgroup = HWGROUP(drive);
                        /*
                         * Ugh.. this part looks ugly because we MUST set up
                         * the interrupt handler before outputting the first block
                         * of data to be written.  If we hit an error (corrupted buffer list)
                         * in ide_multwrite(), then we need to remove the handler/timer
                         * before returning.  Fortunately, this NEVER happens (right?).
                         *
                         * Except when you get an error it seems...
                         */
                        hwgroup->;wrq = *rq; /* scratchpad */
                        ide_set_handler (drive, &amp;multwrite_intr, WAIT_CMD, NULL);
                        if (ide_multwrite(drive, drive->;mult_count)) {
                                unsigned long flags;
                                spin_lock_irqsave(&amp;io_request_lock, flags);
                                hwgroup->;handler = NULL;
                                del_timer(&amp;hwgroup->;timer);
                                spin_unlock_irqrestore(&amp;io_request_lock, flags);
                                return ide_stopped;
                        }
                } else {
                        ide_set_handler (drive, &amp;write_intr, WAIT_CMD, NULL);
                        idedisk_output_data(drive, rq->;buffer, SECTOR_WORDS); 写入一扇区SECTOR_WORDS=512/4
                }
                return ide_started;
        }
        printk(KERN_ERR "%s: bad command: %d\n", drive->;name, rq->;cmd);
        ide_end_request(0, HWGROUP(drive));
        return ide_stopped;
}
void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler,
                      unsigned int timeout, ide_expiry_t *expiry)
{
        unsigned long flags;
        ide_hwgroup_t *hwgroup = HWGROUP(drive);

        spin_lock_irqsave(&amp;io_request_lock, flags);
        if (hwgroup->;handler != NULL) {
                printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
                        drive->;name, hwgroup->;handler, handler);
        }
        hwgroup->;handler        = handler;
        hwgroup->;expiry                = expiry;
        hwgroup->;timer.expires        = jiffies + timeout;
        add_timer(&amp;hwgroup->;timer);
        spin_unlock_irqrestore(&amp;io_request_lock, flags);
}
static inline void idedisk_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
{
        if (drive->;bswap) {
                idedisk_bswap_data(buffer, wcount);
                ide_output_data(drive, buffer, wcount);
                idedisk_bswap_data(buffer, wcount);
        } else
                ide_output_data(drive, buffer, wcount);
}
void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
{
        byte io_32bit = drive->;io_32bit;

        if (io_32bit) {
#if SUPPORT_VLB_SYNC
                if (io_32bit &amp; 2) {
                        unsigned long flags;
                        __save_flags(flags);        /* local CPU only */
                        __cli();                /* local CPU only */
                        do_vlb_sync(IDE_NSECTOR_REG);
                        outsl(IDE_DATA_REG, buffer, wcount);
                        __restore_flags(flags);        /* local CPU only */
                } else
#endif /* SUPPORT_VLB_SYNC */
                        outsl(IDE_DATA_REG, buffer, wcount);
        } else {
#if SUPPORT_SLOW_DATA_PORTS
                if (drive->;slow) {
                        unsigned short *ptr = (unsigned short *) buffer;
                        while (wcount--) {
                                outw_p(*ptr++, IDE_DATA_REG);
                                outw_p(*ptr++, IDE_DATA_REG);
                        }
                } else
#endif /* SUPPORT_SLOW_DATA_PORTS */
                        outsw(IDE_DATA_REG, buffer, wcount<<1);
        }
}
int ide_multwrite (ide_drive_t *drive, unsigned int mcount)
{
        ide_hwgroup_t        *hwgroup= HWGROUP(drive);

        /*
         *        This may look a bit odd, but remember wrq is a copy of the
         *        request not the original. The pointers are real however so the
         *        bh's are not copies. Remember that or bad stuff will happen
         *
         *        At the point we are called the drive has asked us for the
         *        data, and its our job to feed it, walking across bh boundaries
         *        if need be.
         */

        struct request        *rq = &amp;hwgroup->;wrq;

          do {
                unsigned long flags;
                  unsigned int nsect = rq->;current_nr_sectors;
                if (nsect >; mcount)
                        nsect = mcount;
                mcount -= nsect;
                ; 这时mcount为剩余的必需传送的扇区数
                idedisk_output_data(drive, rq->;buffer, nsect<<7);
                spin_lock_irqsave(&amp;io_request_lock, flags);        /* Is this really necessary? */
#ifdef CONFIG_BLK_DEV_PDC4030
                rq->;sector += nsect;
#endif
                if (((long)(rq->;nr_sectors -= nsect)) <= 0)
                        spin_unlock_irqrestore(&amp;io_request_lock, flags);
                        break;
                }
                if ((rq->;current_nr_sectors -= nsect) == 0) {
                        if ((rq->;bh = rq->;bh->;b_reqnext) != NULL) {{
                                rq->;current_nr_sectors = rq->;bh->;b_size>;>;9;
                                rq->;buffer             = rq->;bh->;b_data;
                        } else {
                                spin_unlock_irqrestore(&amp;io_request_lock, flags);
                                printk("%s: buffer list corrupted (%ld, %ld, %d)\n",
                                        drive->;name, rq->;current_nr_sectors,
                                        rq->;nr_sectors, nsect);
                                ide_end_request(0, hwgroup);
                                return 1;
                        }
                } else {
                        /* Fix the pointer.. we ate data */
                        rq->;buffer += nsect << 9;
                }
                spin_unlock_irqrestore(&amp;io_request_lock, flags);
        } while (mcount);
        return 0;
}

; IDE接口共用中断入口
void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
{
        unsigned long flags;
        ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)dev_id;
        ide_hwif_t *hwif;
        ide_drive_t *drive;
        ide_handler_t *handler;
        ide_startstop_t startstop;

        spin_lock_irqsave(&amp;io_request_lock, flags);
        hwif = hwgroup->;hwif;

        if (!ide_ack_intr(hwif)) {
                spin_unlock_irqrestore(&amp;io_request_lock, flags);
                return;
        }

        if ((handler = hwgroup->;handler) == NULL || hwgroup->;poll_timeout != 0) {
                /*
                 * Not expecting an interrupt from this drive.
                 * That means this could be:
                 *        (1) an interrupt from another PCI device
                 *        sharing the same PCI INT# as us.
                 * or        (2) a drive just entered sleep or standby mode,
                 *        and is interrupting to let us know.
                 * or        (3) a spurious interrupt of unknown origin.
                 *
                 * For PCI, we cannot tell the difference,
                 * so in that case we just ignore it and hope it goes away.
                 */
#ifdef CONFIG_BLK_DEV_IDEPCI
                if (IDE_PCI_DEVID_EQ(hwif->;pci_devid, IDE_PCI_DEVID_NULL))
#endif        /* CONFIG_BLK_DEV_IDEPCI */
                {
                        /*
                         * Probably not a shared PCI interrupt,
                         * so we can safely try to do something about it:
                         */
                        unexpected_intr(irq, hwgroup);
#ifdef CONFIG_BLK_DEV_IDEPCI
                } else {
                        /*
                         * Whack the status register, just in case we have a leftover pending IRQ.
                         */
                        (void) IN_BYTE(hwif->;io_ports[IDE_STATUS_OFFSET]);
#endif /* CONFIG_BLK_DEV_IDEPCI */
                }
                spin_unlock_irqrestore(&amp;io_request_lock, flags);
                return;
        }
        drive = hwgroup->;drive;
        if (!drive) {
                /*
                 * This should NEVER happen, and there isn't much we could do about it here.
                 */
                spin_unlock_irqrestore(&amp;io_request_lock, flags);
                return;
        }
        if (!drive_is_ready(drive)) {
                /*
                 * This happens regularly when we share a PCI IRQ with another device.
                 * Unfortunately, it can also happen with some buggy drives that trigger
                 * the IRQ before their status register is up to date.  Hopefully we have
                 * enough advance overhead that the latter isn't a problem.
                 */
                spin_unlock_irqrestore(&amp;io_request_lock, flags);
                return;
        }
        if (!hwgroup->;busy) {
                hwgroup->;busy = 1;        /* paranoia */
                printk("%s: ide_intr: hwgroup->;busy was 0 ??\n", drive->;name);
        }
        hwgroup->;handler = NULL;
        del_timer(&amp;hwgroup->;timer);
        spin_unlock(&amp;io_request_lock);

        if (drive->;unmask)
                ide__sti();        /* local CPU only */
        startstop = handler(drive);                /* service this interrupt, may set handler for next interrupt */
        spin_lock_irq(&amp;io_request_lock);

        /*
         * Note that handler() may have set things up for another
         * interrupt to occur soon, but it cannot happen until
         * we exit from this routine, because it will be the
         * same irq as is currently being serviced here, and Linux
         * won't allow another of the same (on any CPU) until we return.
         */
        set_recovery_timer(HWIF(drive));
        drive->;service_time = jiffies - drive->;service_start;
        if (startstop == ide_stopped) {
                if (hwgroup->;handler == NULL) {        /* paranoia */
                        hwgroup->;busy = 0;
                        ide_do_request(hwgroup, hwif->;irq);
                } else {
                        printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->;name);
                }
        }
        spin_unlock_irqrestore(&amp;io_request_lock, flags);
}
; 单个扇区写入之后的中断处理
static ide_startstop_t write_intr (ide_drive_t *drive)
{
        byte stat;
        int i;
        ide_hwgroup_t *hwgroup = HWGROUP(drive);
        struct request *rq = hwgroup->;rq;

        if (!OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->;bad_wstat)) {
                printk("%s: write_intr error1: nr_sectors=%ld, stat=0x%02x\n", drive->;name, rq->;nr_sectors, stat);
        } else {
                if ((rq->;nr_sectors == 1) ^ ((stat &amp; DRQ_STAT) != 0)) {
                        rq->;sector++;
                        rq->;buffer += 512;
                        rq->;errors = 0;
                        i = --rq->;nr_sectors;
                        --rq->;current_nr_sectors;
                        if (((long)rq->;current_nr_sectors) <= 0)
                                ide_end_request(1, hwgroup);
                        if (i >; 0) {
                                idedisk_output_data (drive, rq->;buffer, SECTOR_WORDS);
                                ide_set_handler (drive, &amp;write_intr, WAIT_CMD, NULL);
                                return ide_started;
                        }
                        return ide_stopped;
                }
                return ide_stopped;        /* the original code did this here (?) */
        }
        return ide_error(drive, "write_intr", stat);
}
; 多重扇区写入后的中断处理
static ide_startstop_t multwrite_intr (ide_drive_t *drive)
{
        byte stat;
        int i;
        ide_hwgroup_t *hwgroup = HWGROUP(drive);
        struct request *rq = &amp;hwgroup->;wrq;

        if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->;bad_wstat)) {
                if (stat &amp; DRQ_STAT) {
                        /*
                         *        The drive wants data. Remember rq is the copy
                         *        of the request
                         */
                        if (rq->;nr_sectors) {
                                if (ide_multwrite(drive, drive->;mult_count))
                                        return ide_stopped;
                                ide_set_handler (drive, &amp;multwrite_intr, WAIT_CMD, NULL);
                                return ide_started;
                        }
                } else {
                        /*
                         *        If the copy has all the blocks completed then
                         *        we can end the original request.
                         */
                        if (!rq->;nr_sectors) {        /* all done? */
                                rq = hwgroup->;rq;
                                for (i = rq->;nr_sectors; i >; 0{
                                        i -= rq->;current_nr_sectors;
                                        ide_end_request(1, hwgroup);
                                }
                                return ide_stopped;
                        }
                }
                return ide_stopped;        /* the original code did this here (?) */
        }
        return ide_error(drive, "multwrite_intr", stat);
}
; 读扇区的中断处理
static ide_startstop_t read_intr (ide_drive_t *drive)
{
        byte stat;
        int i;
        unsigned int msect, nsect;
        struct request *rq;

        /* new way for dealing with premature shared PCI interrupts */
        if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
                if (stat &amp; (ERR_STAT|DRQ_STAT)) {
                        return ide_error(drive, "read_intr", stat);
                }
                /* no data yet, so wait for another interrupt */
                ide_set_handler(drive, &amp;read_intr, WAIT_CMD, NULL);
                return ide_started;
        }
        msect = drive->;mult_count;
       
read_next:
        rq = HWGROUP(drive)->;rq;
        if (msect) {
                if ((nsect = rq->;current_nr_sectors) >; msect)
                        nsect = msect;
                msect -= nsect;
        } else
                nsect = 1;
        idedisk_input_data(drive, rq->;buffer, nsect * SECTOR_WORDS);
        rq->;sector += nsect;
        rq->;buffer += nsect<<9;
        rq->;errors = 0;
        i = (rq->;nr_sectors -= nsect);
        if (((long)(rq->;current_nr_sectors -= nsect)) <= 0)
                ide_end_request(1, HWGROUP(drive));
        if (i >; 0) {
                if (msect)
                        goto read_next;
                ide_set_handler (drive, &amp;read_intr, WAIT_CMD, NULL);
                return ide_started;
        }
        return ide_stopped;
}
static inline void idedisk_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
{
        ide_input_data(drive, buffer, wcount);
        if (drive->;bswap)
                idedisk_bswap_data(buffer, wcount);
}
void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
{
        byte io_32bit = drive->;io_32bit;

        if (io_32bit) {
#if SUPPORT_VLB_SYNC
                if (io_32bit &amp; 2) {
                        unsigned long flags;
                        __save_flags(flags);        /* local CPU only */
                        __cli();                /* local CPU only */
                        do_vlb_sync(IDE_NSECTOR_REG);
                        insl(IDE_DATA_REG, buffer, wcount);
                        __restore_flags(flags);        /* local CPU only */
                } else
#endif /* SUPPORT_VLB_SYNC */
                        insl(IDE_DATA_REG, buffer, wcount);
        } else {
#if SUPPORT_SLOW_DATA_PORTS
                if (drive->;slow) {
                        unsigned short *ptr = (unsigned short *) buffer;
                        while (wcount--) {
                                *ptr++ = inw_p(IDE_DATA_REG);
                                *ptr++ = inw_p(IDE_DATA_REG);
                        }
                } else
#endif /* SUPPORT_SLOW_DATA_PORTS */
                        insw(IDE_DATA_REG, buffer, wcount<<1);
        }
}


atomic_t queued_sectors;

#define blk_finished_io(nsects)                                \
        atomic_sub(nsects, &amp;queued_sectors);                \
        if (atomic_read(&amp;queued_sectors) < 0) {                \
                printk("block: queued_sectors < 0\n";        \
                atomic_set(&amp;queued_sectors, 0);                \
        }

static inline void blkdev_dequeue_request(struct request * req)
{
        list_del(&amp;req->;queue);
}
void ide_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
{
        struct request *rq;
        unsigned long flags;

        spin_lock_irqsave(&amp;io_request_lock, flags);
        rq = hwgroup->;rq;

        if (!end_that_request_first(rq, uptodate, hwgroup->;drive->;name)) {
                add_blkdev_randomness(MAJOR(rq->;rq_dev));
                blkdev_dequeue_request(rq);
                hwgroup->;rq = NULL;
                end_that_request_last(rq);
        }
        spin_unlock_irqrestore(&amp;io_request_lock, flags);
}
int end_that_request_first (struct request *req, int uptodate, char *name)
{
        struct buffer_head * bh;
        int nsect;

        req->;errors = 0;
        if (!uptodate)
                printk("end_request: I/O error, dev %s (%s), sector %lu\n",
                        kdevname(req->;rq_dev), name, req->;sector);

        if ((bh = req->;bh) != NULL) {
                nsect = bh->;b_size >;>; 9;
                blk_finished_io(nsect);
                req->;bh = bh->;b_reqnext;
                bh->;b_reqnext = NULL;
                bh->;b_end_io(bh, uptodate);
                if ((bh = req->;bh) != NULL) {
                        req->;hard_sector += nsect;
                        req->;hard_nr_sectors -= nsect;
                        req->;sector = req->;hard_sector;
                        req->;nr_sectors = req->;hard_nr_sectors;

                        req->;current_nr_sectors = bh->;b_size >;>; 9;
                        if (req->;nr_sectors < req->;current_nr_sectors) {
                                req->;nr_sectors = req->;current_nr_sectors;
                                printk("end_request: buffer-list destroyed\n";
                        }
                        req->;buffer = bh->;b_data;
                        return 1;
                }
        }
        return 0;
}
void end_that_request_last(struct request *req)
{
        if (req->;sem != NULL)
                up(req->;sem);

        blkdev_release_request(req);
}
void inline blkdev_release_request(struct request *req)
{
        request_queue_t *q = req->;q;
        int rw = req->;cmd;

        req->;rq_status = RQ_INACTIVE;
        req->;q = NULL;

        /*
         * Request may not have originated from ll_rw_blk. if not,
         * asumme it has free buffers and check waiters
         */
        if (q) {
                /*
                 * we've released enough buffers to start I/O again
                 */
                if (waitqueue_active(&amp;blk_buffers_wait)
                    &amp;&amp; atomic_read(&amp;queued_sectors) < low_queued_sectors)
                        wake_up(&amp;blk_buffers_wait);

                /*
                 * Add to pending free list and batch wakeups
                 */
                list_add(&amp;req->;table, &amp;q->;pending_freelist[rw]);

                if (++q->;pending_free[rw] >;= batch_requests) {
                        int wake_up = q->;pending_free[rw];
                        blk_refill_freelist(q, rw);
                        wake_up_nr(&amp;q->;wait_for_request, wake_up);
                }
        }
}
void inline blk_refill_freelist(request_queue_t *q, int rw)
{
        if (q->;pending_free[rw]) {
                list_splice(&amp;q->;pending_freelist[rw], &amp;q->;request_freelist[rw]);
                INIT_LIST_HEAD(&amp;q->;pending_freelist[rw]);
                q->;pending_free[rw] = 0;
        }
}
</code>;

论坛徽章:
0
2 [报告]
发表于 2003-02-11 09:39 |只看该作者

[转贴]分析IDE硬盘驱动器读写过程

yangwen

论坛徽章:
0
3 [报告]
发表于 2003-07-11 21:10 |只看该作者

[转贴]分析IDE硬盘驱动器读写过程

bucuo
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP