ide: use lock bitops for ports serialization (v2)
* Add ->host_busy field to struct ide_host and use it's first bit together with lock bitops to provide new ports serialization method. * Convert core IDE code to use new ide_[un]lock_host() helpers. This removes the need for taking hwgroup->lock if host is already busy on serialized hosts and makes it possible to merge ide_hwgroup_t into ide_hwif_t (done in the later patch). * Remove no longer needed ide_hwgroup_t.busy and ide_[un]lock_hwgroup(). * Update do_ide_request() documentation. v2: * ide_release_lock() should be called inside IDE_HFLAG_SERIALIZE check. * Add ide_hwif_t.busy flag and ide_[un]lock_port() for serializing devices on a port. Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
This commit is contained in:
parent
efe0397eef
commit
5b31f855f1
|
@ -666,45 +666,54 @@ void ide_stall_queue (ide_drive_t *drive, unsigned long timeout)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ide_stall_queue);
|
EXPORT_SYMBOL(ide_stall_queue);
|
||||||
|
|
||||||
|
static inline int ide_lock_port(ide_hwif_t *hwif)
|
||||||
|
{
|
||||||
|
if (hwif->busy)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
hwif->busy = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ide_unlock_port(ide_hwif_t *hwif)
|
||||||
|
{
|
||||||
|
hwif->busy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int ide_lock_host(struct ide_host *host, ide_hwif_t *hwif)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
if (host->host_flags & IDE_HFLAG_SERIALIZE) {
|
||||||
|
rc = test_and_set_bit_lock(IDE_HOST_BUSY, &host->host_busy);
|
||||||
|
if (rc == 0) {
|
||||||
|
/* for atari only */
|
||||||
|
ide_get_lock(ide_intr, hwif);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ide_unlock_host(struct ide_host *host)
|
||||||
|
{
|
||||||
|
if (host->host_flags & IDE_HFLAG_SERIALIZE) {
|
||||||
|
/* for atari only */
|
||||||
|
ide_release_lock();
|
||||||
|
clear_bit_unlock(IDE_HOST_BUSY, &host->host_busy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Issue a new request to a drive from hwgroup
|
* Issue a new request to a drive from hwgroup
|
||||||
*
|
|
||||||
* A hwgroup is a serialized group of IDE interfaces. Usually there is
|
|
||||||
* exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640)
|
|
||||||
* may have both interfaces in a single hwgroup to "serialize" access.
|
|
||||||
* Or possibly multiple ISA interfaces can share a common IRQ by being grouped
|
|
||||||
* together into one hwgroup for serialized access.
|
|
||||||
*
|
|
||||||
* Note also that several hwgroups can end up sharing a single IRQ,
|
|
||||||
* possibly along with many other devices. This is especially common in
|
|
||||||
* PCI-based systems with off-board IDE controller cards.
|
|
||||||
*
|
|
||||||
* The IDE driver uses a per-hwgroup lock to protect the hwgroup->busy flag.
|
|
||||||
*
|
|
||||||
* The first thread into the driver for a particular hwgroup sets the
|
|
||||||
* hwgroup->busy flag to indicate that this hwgroup is now active,
|
|
||||||
* and then initiates processing of the top request from the request queue.
|
|
||||||
*
|
|
||||||
* Other threads attempting entry notice the busy setting, and will simply
|
|
||||||
* queue their new requests and exit immediately. Note that hwgroup->busy
|
|
||||||
* remains set even when the driver is merely awaiting the next interrupt.
|
|
||||||
* Thus, the meaning is "this hwgroup is busy processing a request".
|
|
||||||
*
|
|
||||||
* When processing of a request completes, the completing thread or IRQ-handler
|
|
||||||
* will start the next request from the queue. If no more work remains,
|
|
||||||
* the driver will clear the hwgroup->busy flag and exit.
|
|
||||||
*
|
|
||||||
* The per-hwgroup spinlock is used to protect all access to the
|
|
||||||
* hwgroup->busy flag, but is otherwise not needed for most processing in
|
|
||||||
* the driver. This makes the driver much more friendlier to shared IRQs
|
|
||||||
* than previous designs, while remaining 100% (?) SMP safe and capable.
|
|
||||||
*/
|
*/
|
||||||
void do_ide_request(struct request_queue *q)
|
void do_ide_request(struct request_queue *q)
|
||||||
{
|
{
|
||||||
ide_drive_t *drive = q->queuedata;
|
ide_drive_t *drive = q->queuedata;
|
||||||
ide_hwif_t *hwif = drive->hwif;
|
ide_hwif_t *hwif = drive->hwif;
|
||||||
|
struct ide_host *host = hwif->host;
|
||||||
ide_hwgroup_t *hwgroup = hwif->hwgroup;
|
ide_hwgroup_t *hwgroup = hwif->hwgroup;
|
||||||
struct request *rq;
|
struct request *rq = NULL;
|
||||||
ide_startstop_t startstop;
|
ide_startstop_t startstop;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -721,9 +730,13 @@ void do_ide_request(struct request_queue *q)
|
||||||
blk_remove_plug(q);
|
blk_remove_plug(q);
|
||||||
|
|
||||||
spin_unlock_irq(q->queue_lock);
|
spin_unlock_irq(q->queue_lock);
|
||||||
|
|
||||||
|
if (ide_lock_host(host, hwif))
|
||||||
|
goto plug_device_2;
|
||||||
|
|
||||||
spin_lock_irq(&hwgroup->lock);
|
spin_lock_irq(&hwgroup->lock);
|
||||||
|
|
||||||
if (!ide_lock_hwgroup(hwgroup, hwif)) {
|
if (!ide_lock_port(hwif)) {
|
||||||
ide_hwif_t *prev_port;
|
ide_hwif_t *prev_port;
|
||||||
repeat:
|
repeat:
|
||||||
prev_port = hwif->host->cur_port;
|
prev_port = hwif->host->cur_port;
|
||||||
|
@ -731,7 +744,7 @@ repeat:
|
||||||
|
|
||||||
if (drive->dev_flags & IDE_DFLAG_SLEEPING) {
|
if (drive->dev_flags & IDE_DFLAG_SLEEPING) {
|
||||||
if (time_before(drive->sleep, jiffies)) {
|
if (time_before(drive->sleep, jiffies)) {
|
||||||
ide_unlock_hwgroup(hwgroup);
|
ide_unlock_port(hwif);
|
||||||
goto plug_device;
|
goto plug_device;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -761,7 +774,7 @@ repeat:
|
||||||
spin_lock_irq(&hwgroup->lock);
|
spin_lock_irq(&hwgroup->lock);
|
||||||
|
|
||||||
if (!rq) {
|
if (!rq) {
|
||||||
ide_unlock_hwgroup(hwgroup);
|
ide_unlock_port(hwif);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -782,7 +795,7 @@ repeat:
|
||||||
blk_pm_request(rq) == 0 &&
|
blk_pm_request(rq) == 0 &&
|
||||||
(rq->cmd_flags & REQ_PREEMPT) == 0) {
|
(rq->cmd_flags & REQ_PREEMPT) == 0) {
|
||||||
/* there should be no pending command at this point */
|
/* there should be no pending command at this point */
|
||||||
ide_unlock_hwgroup(hwgroup);
|
ide_unlock_port(hwif);
|
||||||
goto plug_device;
|
goto plug_device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -798,11 +811,15 @@ repeat:
|
||||||
goto plug_device;
|
goto plug_device;
|
||||||
out:
|
out:
|
||||||
spin_unlock_irq(&hwgroup->lock);
|
spin_unlock_irq(&hwgroup->lock);
|
||||||
|
if (rq == NULL)
|
||||||
|
ide_unlock_host(host);
|
||||||
spin_lock_irq(q->queue_lock);
|
spin_lock_irq(q->queue_lock);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
plug_device:
|
plug_device:
|
||||||
spin_unlock_irq(&hwgroup->lock);
|
spin_unlock_irq(&hwgroup->lock);
|
||||||
|
ide_unlock_host(host);
|
||||||
|
plug_device_2:
|
||||||
spin_lock_irq(q->queue_lock);
|
spin_lock_irq(q->queue_lock);
|
||||||
|
|
||||||
if (!elv_queue_empty(q))
|
if (!elv_queue_empty(q))
|
||||||
|
@ -844,9 +861,9 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error)
|
||||||
ide_dma_off_quietly(drive);
|
ide_dma_off_quietly(drive);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* un-busy drive etc (hwgroup->busy is cleared on return) and
|
* un-busy drive etc and make sure request is sane
|
||||||
* make sure request is sane
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
rq = HWGROUP(drive)->rq;
|
rq = HWGROUP(drive)->rq;
|
||||||
|
|
||||||
if (!rq)
|
if (!rq)
|
||||||
|
@ -895,6 +912,7 @@ static void ide_plug_device(ide_drive_t *drive)
|
||||||
void ide_timer_expiry (unsigned long data)
|
void ide_timer_expiry (unsigned long data)
|
||||||
{
|
{
|
||||||
ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
|
ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
|
||||||
|
ide_hwif_t *uninitialized_var(hwif);
|
||||||
ide_drive_t *uninitialized_var(drive);
|
ide_drive_t *uninitialized_var(drive);
|
||||||
ide_handler_t *handler;
|
ide_handler_t *handler;
|
||||||
ide_expiry_t *expiry;
|
ide_expiry_t *expiry;
|
||||||
|
@ -918,7 +936,6 @@ void ide_timer_expiry (unsigned long data)
|
||||||
printk(KERN_ERR "%s: ->cur_dev was NULL\n", __func__);
|
printk(KERN_ERR "%s: ->cur_dev was NULL\n", __func__);
|
||||||
hwgroup->handler = NULL;
|
hwgroup->handler = NULL;
|
||||||
} else {
|
} else {
|
||||||
ide_hwif_t *hwif;
|
|
||||||
ide_startstop_t startstop = ide_stopped;
|
ide_startstop_t startstop = ide_stopped;
|
||||||
|
|
||||||
if ((expiry = hwgroup->expiry) != NULL) {
|
if ((expiry = hwgroup->expiry) != NULL) {
|
||||||
|
@ -964,15 +981,17 @@ void ide_timer_expiry (unsigned long data)
|
||||||
spin_lock_irq(&hwgroup->lock);
|
spin_lock_irq(&hwgroup->lock);
|
||||||
enable_irq(hwif->irq);
|
enable_irq(hwif->irq);
|
||||||
if (startstop == ide_stopped) {
|
if (startstop == ide_stopped) {
|
||||||
ide_unlock_hwgroup(hwgroup);
|
ide_unlock_port(hwif);
|
||||||
plug_device = 1;
|
plug_device = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&hwgroup->lock, flags);
|
spin_unlock_irqrestore(&hwgroup->lock, flags);
|
||||||
|
|
||||||
if (plug_device)
|
if (plug_device) {
|
||||||
|
ide_unlock_host(hwif->host);
|
||||||
ide_plug_device(drive);
|
ide_plug_device(drive);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1150,7 +1169,7 @@ irqreturn_t ide_intr (int irq, void *dev_id)
|
||||||
*/
|
*/
|
||||||
if (startstop == ide_stopped) {
|
if (startstop == ide_stopped) {
|
||||||
if (hwgroup->handler == NULL) { /* paranoia */
|
if (hwgroup->handler == NULL) { /* paranoia */
|
||||||
ide_unlock_hwgroup(hwgroup);
|
ide_unlock_port(hwif);
|
||||||
plug_device = 1;
|
plug_device = 1;
|
||||||
} else
|
} else
|
||||||
printk(KERN_ERR "%s: %s: huh? expected NULL handler "
|
printk(KERN_ERR "%s: %s: huh? expected NULL handler "
|
||||||
|
@ -1161,8 +1180,10 @@ out_handled:
|
||||||
out:
|
out:
|
||||||
spin_unlock_irqrestore(&hwgroup->lock, flags);
|
spin_unlock_irqrestore(&hwgroup->lock, flags);
|
||||||
out_early:
|
out_early:
|
||||||
if (plug_device)
|
if (plug_device) {
|
||||||
|
ide_unlock_host(hwif->host);
|
||||||
ide_plug_device(drive);
|
ide_plug_device(drive);
|
||||||
|
}
|
||||||
|
|
||||||
return irq_ret;
|
return irq_ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -828,6 +828,7 @@ typedef struct hwif_s {
|
||||||
|
|
||||||
unsigned present : 1; /* this interface exists */
|
unsigned present : 1; /* this interface exists */
|
||||||
unsigned sg_mapped : 1; /* sg_table and sg_nents are ready */
|
unsigned sg_mapped : 1; /* sg_table and sg_nents are ready */
|
||||||
|
unsigned busy : 1; /* serializes devices on a port */
|
||||||
|
|
||||||
struct device gendev;
|
struct device gendev;
|
||||||
struct device *portdev;
|
struct device *portdev;
|
||||||
|
@ -851,8 +852,13 @@ struct ide_host {
|
||||||
unsigned long host_flags;
|
unsigned long host_flags;
|
||||||
void *host_priv;
|
void *host_priv;
|
||||||
ide_hwif_t *cur_port; /* for hosts requiring serialization */
|
ide_hwif_t *cur_port; /* for hosts requiring serialization */
|
||||||
|
|
||||||
|
/* used for hosts requiring serialization */
|
||||||
|
volatile long host_busy;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define IDE_HOST_BUSY 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* internal ide interrupt handler type
|
* internal ide interrupt handler type
|
||||||
*/
|
*/
|
||||||
|
@ -866,8 +872,6 @@ typedef struct hwgroup_s {
|
||||||
/* irq handler, if active */
|
/* irq handler, if active */
|
||||||
ide_startstop_t (*handler)(ide_drive_t *);
|
ide_startstop_t (*handler)(ide_drive_t *);
|
||||||
|
|
||||||
/* BOOL: protects all fields below */
|
|
||||||
volatile int busy;
|
|
||||||
/* BOOL: polling active & poll_timeout field valid */
|
/* BOOL: polling active & poll_timeout field valid */
|
||||||
unsigned int polling : 1;
|
unsigned int polling : 1;
|
||||||
|
|
||||||
|
@ -1271,26 +1275,6 @@ extern void ide_stall_queue(ide_drive_t *drive, unsigned long timeout);
|
||||||
|
|
||||||
extern void ide_timer_expiry(unsigned long);
|
extern void ide_timer_expiry(unsigned long);
|
||||||
extern irqreturn_t ide_intr(int irq, void *dev_id);
|
extern irqreturn_t ide_intr(int irq, void *dev_id);
|
||||||
|
|
||||||
static inline int ide_lock_hwgroup(ide_hwgroup_t *hwgroup, ide_hwif_t *hwif)
|
|
||||||
{
|
|
||||||
if (hwgroup->busy)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
hwgroup->busy = 1;
|
|
||||||
/* for atari only */
|
|
||||||
ide_get_lock(ide_intr, hwif);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void ide_unlock_hwgroup(ide_hwgroup_t *hwgroup)
|
|
||||||
{
|
|
||||||
/* for atari only */
|
|
||||||
ide_release_lock();
|
|
||||||
hwgroup->busy = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void do_ide_request(struct request_queue *);
|
extern void do_ide_request(struct request_queue *);
|
||||||
|
|
||||||
void ide_init_disk(struct gendisk *, ide_drive_t *);
|
void ide_init_disk(struct gendisk *, ide_drive_t *);
|
||||||
|
@ -1617,13 +1601,6 @@ static inline void ide_set_max_pio(ide_drive_t *drive)
|
||||||
|
|
||||||
extern spinlock_t ide_lock;
|
extern spinlock_t ide_lock;
|
||||||
extern struct mutex ide_cfg_mtx;
|
extern struct mutex ide_cfg_mtx;
|
||||||
/*
|
|
||||||
* Structure locking:
|
|
||||||
*
|
|
||||||
* ide_hwgroup_t->busy: hwgroup->lock
|
|
||||||
* ide_hwif_t->{hwgroup,mate}: constant, no locking
|
|
||||||
* ide_drive_t->hwif: constant, no locking
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define local_irq_set(flags) do { local_save_flags((flags)); local_irq_enable_in_hardirq(); } while (0)
|
#define local_irq_set(flags) do { local_save_flags((flags)); local_irq_enable_in_hardirq(); } while (0)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue