SCSI: Fix some locking issues
PATCH 1/1 SCSI: Fix some locking issues
Hi all,
the following patch applies to 2.6.26-rc8. However, the same issues exist in the 2.6.25.y stable tree and the patch applies with offsets to 2.6.25.9; I haven't checked 2.6.24 and before.
Regards,
Elias
From: Elias Oltmanns <[email protected]>
Subject: SCSI: Fix some locking issues
Make sure that host_blocked is consistently protected by the host_lock. Similarly, device_block has to be protected by the queue_lock. Also, blk_plug_device() has to be called with the queue_lock held in scsi_host_queue_ready().
---
drivers/scsi/scsi.c | 6 ++++++ drivers/scsi/scsi_lib.c | 13 +++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-)
drivers/scsi/scsi.c +6 -0
@@ -826,8 +826,10 @@ void scsi_finish_command(struct scsi_cmnd *cmd) {struct scsi_device *sdev = cmd->device;struct Scsi_Host *shost = sdev->host; +struct request_queue *q = sdev->request_queue;struct scsi_driver *drv;unsigned int good_bytes; +unsigned long flags;scsi_device_unbusy (sdev);
@@ -839,8 +841,12 @@ void scsi_finish_command(struct scsi_cmnd *cmd)* * XXX(hch): What about locking? */ +spin_lock_irqsave (shost->host_lock, flags); shost->host_blocked =0 ; +spin_unlock (shost->host_lock); +spin_lock (q->queue_lock); sdev->device_blocked =0 ; +spin_unlock_irqrestore (q->queue_lock, flags);/* * If we have valid sense information, then some kind of recovery
drivers/scsi/scsi_lib.c +11 -2
@@ -133,10 +133,15 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason)* if a command is requeued with no other commands outstanding * either for the device or for the host. */ -if (reason ==SCSI_MLQUEUE_HOST_BUSY ) +if (reason ==SCSI_MLQUEUE_HOST_BUSY ) { +spin_lock_irqsave (host->host_lock, flags); host->host_blocked = host->max_host_blocked; -else if (reason ==SCSI_MLQUEUE_DEVICE_BUSY ) +spin_unlock_irqrestore (host->host_lock, flags); + }else if (reason ==SCSI_MLQUEUE_DEVICE_BUSY ) { +spin_lock_irqsave (q->queue_lock, flags); device->device_blocked = device->max_device_blocked; +spin_unlock_irqrestore (q->queue_lock, flags); + }/* * Decrement the counters, since these commands are no longer
@@ -1320,7 +1325,11 @@ static inline int scsi_host_queue_ready(struct request_queue *q,printk ("scsi%d unblocking host at zero depth\n" , shost->host_no)); }else { +spin_unlock (shost->host_lock); +spin_lock (q->queue_lock);blk_plug_device (q); +spin_unlock (q->queue_lock); +spin_lock (shost->host_lock);return 0 ; } }
Hi James,
sorry for bothering you but I've just noticed that the patch below has neither been scheduled for the stable review, nor queued up for Linus. May be you just don't consider this serious enough for these trees but I wanted to make sure that the situation will be dealt with eventualy. The patch applies to 2.6.26-rc8.
Regards,
Elias
From: Elias Oltmanns <[email protected]>
Subject: SCSI: Fix some locking issues
Make sure that host_blocked is consistently protected by the host_lock. Similarly, device_block has to be protected by the queue_lock. Also, blk_plug_device() has to be called with the queue_lock held in scsi_host_queue_ready().
---
drivers/scsi/scsi.c | 6 ++++++ drivers/scsi/scsi_lib.c | 13 +++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-)
drivers/scsi/scsi.c +6 -0
@@ -826,8 +826,10 @@ void scsi_finish_command(struct scsi_cmnd *cmd) {struct scsi_device *sdev = cmd->device;struct Scsi_Host *shost = sdev->host; +struct request_queue *q = sdev->request_queue;struct scsi_driver *drv;unsigned int good_bytes; +unsigned long flags;scsi_device_unbusy (sdev);
@@ -839,8 +841,12 @@ void scsi_finish_command(struct scsi_cmnd *cmd)* * XXX(hch): What about locking? */ +spin_lock_irqsave (shost->host_lock, flags); shost->host_blocked =0 ; +spin_unlock (shost->host_lock); +spin_lock (q->queue_lock); sdev->device_blocked =0 ; +spin_unlock_irqrestore (q->queue_lock, flags);/* * If we have valid sense information, then some kind of recovery
drivers/scsi/scsi_lib.c +11 -2
@@ -133,10 +133,15 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason)* if a command is requeued with no other commands outstanding * either for the device or for the host. */ -if (reason ==SCSI_MLQUEUE_HOST_BUSY ) +if (reason ==SCSI_MLQUEUE_HOST_BUSY ) { +spin_lock_irqsave (host->host_lock, flags); host->host_blocked = host->max_host_blocked; -else if (reason ==SCSI_MLQUEUE_DEVICE_BUSY ) +spin_unlock_irqrestore (host->host_lock, flags); + }else if (reason ==SCSI_MLQUEUE_DEVICE_BUSY ) { +spin_lock_irqsave (q->queue_lock, flags); device->device_blocked = device->max_device_blocked; +spin_unlock_irqrestore (q->queue_lock, flags); + }/* * Decrement the counters, since these commands are no longer
@@ -1320,7 +1325,11 @@ static inline int scsi_host_queue_ready(struct request_queue *q,printk ("scsi%d unblocking host at zero depth\n" , shost->host_no)); }else { +spin_unlock (shost->host_lock); +spin_lock (q->queue_lock);blk_plug_device (q); +spin_unlock (q->queue_lock); +spin_lock (shost->host_lock);return 0 ; } }
James Bottomley
maintainerQuoted 7 lines · Elias Oltmanns
Hi James,
sorry for bothering you but I've just noticed that the patch below has neither been scheduled for the stable review, nor queued up for Linus. May be you just don't consider this serious enough for these trees but I wanted to make sure that the situation will be dealt with eventualy. The patch applies to 2.6.26-rc8.
OK, well at first glance, the locking around device_blocked and host_blocked looks pointless. What are the failure traces you're using to decide they need spinlock protection?
The blk_plug_queue change looks reasonable ... however, blk_plug_queue itself looks like it might not entirely need the queue lock ... I need to investigate more closely.
James
Quoted 10 lines · James Bottomley
Quoted 8 lines · Elias Oltmanns
Hi James,
sorry for bothering you but I've just noticed that the patch below has neither been scheduled for the stable review, nor queued up for Linus. May be you just don't consider this serious enough for these trees but I wanted to make sure that the situation will be dealt with eventualy. The patch applies to 2.6.26-rc8.
Quoted 3 lines
OK, well at first glance, the locking around
device_blockedandhost_blockedlooks pointless. What are the failure traces you're using to decide they need spinlock protection?
scsi_queue_insert() as well as scsi_finish_command() can be called at any time as part of regular command completion or error handling. There is no reason why the ->request_fn() for the same device or for another device on the same host should not be in progress at the same time.
Quoted 4 lines
The
blk_plug_queuechange looks reasonable ... however,blk_plug_queueitself looks like it might not entirely need the queue lock ... I need to investigate more closely.
Well, I rather think it does. We have to serialise access to the unplug_timer and there is a call to __set_bit() which, as I understand, requires the calling function to ensure atomicity.
Regards,
Elias
Quoted 7 lines · Elias Oltmanns
Quoted 3 lines
The
blk_plug_queuechange looks reasonable ... however,blk_plug_queueitself looks like it might not entirely need the queue lock ... I need to investigate more closely.Well, I rather think it does. We have to serialise access to the
unplug_timerand there is a call to__set_bit()which, as I understand, requires the calling function to ensure atomicity.
Yep, blk_plug_device() needs to be called with the queue lock held.
Signature · 1 line
Jens Axboe
James Bottomley
maintainerQuoted 10 lines · Jens Axboe
Quoted 7 lines · Elias Oltmanns
Quoted 3 lines
The
blk_plug_queuechange looks reasonable ... however,blk_plug_queueitself looks like it might not entirely need the queue lock ... I need to investigate more closely.Well, I rather think it does. We have to serialise access to the
unplug_timerand there is a call to__set_bit()which, as I understand, requires the calling function to ensure atomicity.Yep,
blk_plug_device()needs to be called with the queue lock held.
That's what the comment says ... but if you replaced the test_bit with an atomic operation then the rest of it does look to be in no need of serialisation ... unless there's something I missed?
James
Quoted 10 lines · James Bottomley
Quoted 9 lines · Jens Axboe
Quoted 7 lines · Elias Oltmanns
Quoted 3 lines
The
blk_plug_queuechange looks reasonable ... however,blk_plug_queueitself looks like it might not entirely need the queue lock ... I need to investigate more closely.Well, I rather think it does. We have to serialise access to the
unplug_timerand there is a call to__set_bit()which, as I understand, requires the calling function to ensure atomicity.
Quoted 5 lines
Quoted 1 line
Yep,
blk_plug_device()needs to be called with the queue lock held.That's what the comment says ... but if you replaced the
test_bitwith an atomic operation then the rest of it does look to be in no need of serialisation ... unless there's something I missed?
Indeed, but then you would have to use atomic bitops everywhere and that is the bit we moved away from.
Signature · 1 line
Jens Axboe
James Bottomley
maintainerQuoted 11 lines · Jens Axboe
Quoted 10 lines · James Bottomley
Quoted 9 lines · Jens Axboe
Quoted 7 lines · Elias Oltmanns
Quoted 3 lines
The
blk_plug_queuechange looks reasonable ... however,blk_plug_queueitself looks like it might not entirely need the queue lock ... I need to investigate more closely.Well, I rather think it does. We have to serialise access to the
unplug_timerand there is a call to__set_bit()which, as I understand, requires the calling function to ensure atomicity.
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
Yep,
blk_plug_device()needs to be called with the queue lock held.That's what the comment says ... but if you replaced the
test_bitwith an atomic operation then the rest of it does look to be in no need of serialisation ... unless there's something I missed?Indeed, but then you would have to use atomic bitops everywhere and that is the bit we moved away from.
Not necessarily ... only for QUEUE_FLAG_CLUSTER. That's really only in this one place and then the one in blk_remove_plug would have to become test_and_clear_bit. All the other places barring loop_unplug() are only tests (which don't affect the atomicity).
It's just for SCSI the double spin lock followed by double spin unlock to get the locking right is kind of nasty ... I'm just wondering what the universe would look like if it were rendered unnecessary.
James
Quoted 22 lines · James Bottomley
Quoted 12 lines · Jens Axboe
Quoted 10 lines · James Bottomley
Quoted 9 lines · Jens Axboe
Quoted 7 lines · Elias Oltmanns
Quoted 3 lines
The
blk_plug_queuechange looks reasonable ... however,blk_plug_queueitself looks like it might not entirely need the queue lock ... I need to investigate more closely.Well, I rather think it does. We have to serialise access to the
unplug_timerand there is a call to__set_bit()which, as I understand, requires the calling function to ensure atomicity.Quoted 8 lines
Quoted 5 lines
Quoted 1 line
Yep,
blk_plug_device()needs to be called with the queue lock held.That's what the comment says ... but if you replaced the
test_bitwith an atomic operation then the rest of it does look to be in no need of serialisation ... unless there's something I missed?Indeed, but then you would have to use atomic bitops everywhere and that is the bit we moved away from.
Quoted 8 lines
Not necessarily ... only for
QUEUE_FLAG_CLUSTER. That's really only in this one place and then the one inblk_remove_plugwould have to becometest_and_clear_bit. All the other places barringloop_unplug()are only tests (which don't affect the atomicity).It's just for SCSI the double spin lock followed by double spin unlock to get the locking right is kind of nasty ... I'm just wondering what the universe would look like if it were rendered unnecessary.
We have to consider one more thing: Without the locking in blk_plug_device(), the following sequence of events may occur:
1. Some calls blk_plug_device().
2. Someone else calls blk_stop_queue() right after the check in blk_plug_device() has been performed but before the test_and_set_bit() has been called.
3. The unplug_timer expires, __generic_unplug_device() discovers that the queue has been stopped in the meantime and returns without removing the plug.
4. Someone calls blk_start_queue() later on which will execute the ->request_fn() even though blk_queue_plugged() is still true.
In order to resolve this, we'd have to switch the calls to blk_remove_plug() and blk_queue_stopped() in __generic_unplug_device(). I'm not quite sure at the moment whether this would have any further implications.
Regards,
Elias
Quoted 20 lines · Elias Oltmanns
Quoted 19 lines · James Bottomley
Quoted 11 lines · Jens Axboe
Quoted 6 lines · James Bottomley
Quoted 1 line · Jens Axboe
Yep,
blk_plug_device()needs to be called with the queue lock held.That's what the comment says ... but if you replaced the
test_bitwith an atomic operation then the rest of it does look to be in no need of serialisation ... unless there's something I missed?Indeed, but then you would have to use atomic bitops everywhere and that is the bit we moved away from.
Not necessarily ... only for
QUEUE_FLAG_CLUSTER. That's really only in this one place and then the one inblk_remove_plugwould have to becometest_and_clear_bit. All the other places barringloop_unplug()are only tests (which don't affect the atomicity).
Quoted 6 lines
Quoted 3 lines
It's just for SCSI the double spin lock followed by double spin unlock to get the locking right is kind of nasty ... I'm just wondering what the universe would look like if it were rendered unnecessary.
We have to consider one more thing: Without the locking in
blk_plug_device(), the following sequence of events may occur:
Actually, it's worse than that. Locking is required in order to make absolutely sure that the unplug_timer is active iff QUEUE_FLAG_PLUGGED is set. Admittedly, it seems *very* unlikely that blk_remove_plug() will complete before the call to mod_timer() in blk_plug_device() even though it has started only *after* a call to test_and_set_bit(). However, if such a thing would ever happen, it could have dire consequences.
Regards,
Elias
Quoted 28 lines · Elias Oltmanns
Quoted 20 lines · Elias Oltmanns
Quoted 19 lines · James Bottomley
Quoted 11 lines · Jens Axboe
Quoted 6 lines · James Bottomley
Quoted 1 line · Jens Axboe
Yep,
blk_plug_device()needs to be called with the queue lock held.That's what the comment says ... but if you replaced the
test_bitwith an atomic operation then the rest of it does look to be in no need of serialisation ... unless there's something I missed?Indeed, but then you would have to use atomic bitops everywhere and that is the bit we moved away from.
Not necessarily ... only for
QUEUE_FLAG_CLUSTER. That's really only in this one place and then the one inblk_remove_plugwould have to becometest_and_clear_bit. All the other places barringloop_unplug()are only tests (which don't affect the atomicity).Quoted 6 lines
Quoted 3 lines
It's just for SCSI the double spin lock followed by double spin unlock to get the locking right is kind of nasty ... I'm just wondering what the universe would look like if it were rendered unnecessary.
We have to consider one more thing: Without the locking in
blk_plug_device(), the following sequence of events may occur:
Quoted 6 lines
Actually, it's worse than that. Locking is required in order to make absolutely sure that the
unplug_timeris active iffQUEUE_FLAG_PLUGGEDis set. Admittedly, it seems *very* unlikely thatblk_remove_plug()will complete before the call tomod_timer()inblk_plug_device()even though it has started only *after* a call totest_and_set_bit(). However, if such a thing would ever happen, it could have dire consequences.
Both are races possible without either atomic bitops or the queue lock being held. We can't properly mix eg set_bit() and __set_bit(). The plugged bit is the most hammered, so it's staying non-atomic and SCSI will need to provide proper locking there.
Signature · 1 line
Jens Axboe
James Bottomley
maintainerQuoted 36 lines · Jens Axboe
Quoted 28 lines · Elias Oltmanns
Quoted 20 lines · Elias Oltmanns
Quoted 19 lines · James Bottomley
Quoted 11 lines · Jens Axboe
Quoted 6 lines · James Bottomley
Quoted 1 line · Jens Axboe
Yep,
blk_plug_device()needs to be called with the queue lock held.That's what the comment says ... but if you replaced the
test_bitwith an atomic operation then the rest of it does look to be in no need of serialisation ... unless there's something I missed?Indeed, but then you would have to use atomic bitops everywhere and that is the bit we moved away from.
Not necessarily ... only for
QUEUE_FLAG_CLUSTER. That's really only in this one place and then the one inblk_remove_plugwould have to becometest_and_clear_bit. All the other places barringloop_unplug()are only tests (which don't affect the atomicity).Quoted 6 lines
Quoted 3 lines
It's just for SCSI the double spin lock followed by double spin unlock to get the locking right is kind of nasty ... I'm just wondering what the universe would look like if it were rendered unnecessary.
We have to consider one more thing: Without the locking in
blk_plug_device(), the following sequence of events may occur:Quoted 6 lines
Actually, it's worse than that. Locking is required in order to make absolutely sure that the
unplug_timeris active iffQUEUE_FLAG_PLUGGEDis set. Admittedly, it seems *very* unlikely thatblk_remove_plug()will complete before the call tomod_timer()inblk_plug_device()even though it has started only *after* a call totest_and_set_bit(). However, if such a thing would ever happen, it could have dire consequences.
Quoted 4 lines
Both are races possible without either atomic bitops or the queue lock being held. We can't properly mix eg
set_bit()and__set_bit(). The plugged bit is the most hammered, so it's staying non-atomic and SCSI will need to provide proper locking there.
You're the boss.
Actually, after all of this, it looks like the host queue plug is superfluous. If the host actually says not ready from scsi_host_queue_ready() we go to the not ready processing clause in scsi_prep_fn() which actually checks the outstanding on the current device and plugs the queue if there aren't any commands. This is actually more correct behaviour than a blind plug regardless (and it's also done under the queue lock), so I think this is the correct fix.
James
---
drivers/scsi/scsi_lib.c +0 -1
@@ -1337,7 +1337,6 @@ static inline int scsi_host_queue_ready(struct request_queue *q,printk ("scsi%d unblocking host at zero depth\n" , shost->host_no)); }else { -blk_plug_device (q);return 0 ; } }
Quoted 44 lines · James Bottomley
Quoted 36 lines · Jens Axboe
Quoted 28 lines · Elias Oltmanns
Quoted 20 lines · Elias Oltmanns
Quoted 19 lines · James Bottomley
Quoted 11 lines · Jens Axboe
Quoted 6 lines · James Bottomley
Quoted 1 line · Jens Axboe
Yep,
blk_plug_device()needs to be called with the queue lock held.That's what the comment says ... but if you replaced the
test_bitwith an atomic operation then the rest of it does look to be in no need of serialisation ... unless there's something I missed?Indeed, but then you would have to use atomic bitops everywhere and that is the bit we moved away from.
Not necessarily ... only for
QUEUE_FLAG_CLUSTER. That's really only in this one place and then the one inblk_remove_plugwould have to becometest_and_clear_bit. All the other places barringloop_unplug()are only tests (which don't affect the atomicity).Quoted 6 lines
Quoted 3 lines
It's just for SCSI the double spin lock followed by double spin unlock to get the locking right is kind of nasty ... I'm just wondering what the universe would look like if it were rendered unnecessary.
We have to consider one more thing: Without the locking in
blk_plug_device(), the following sequence of events may occur:Quoted 6 lines
Actually, it's worse than that. Locking is required in order to make absolutely sure that the
unplug_timeris active iffQUEUE_FLAG_PLUGGEDis set. Admittedly, it seems *very* unlikely thatblk_remove_plug()will complete before the call tomod_timer()inblk_plug_device()even though it has started only *after* a call totest_and_set_bit(). However, if such a thing would ever happen, it could have dire consequences.Quoted 4 lines
Both are races possible without either atomic bitops or the queue lock being held. We can't properly mix eg
set_bit()and__set_bit(). The plugged bit is the most hammered, so it's staying non-atomic and SCSI will need to provide proper locking there.You're the boss.
Quoted 7 lines
Actually, after all of this, it looks like the host queue plug is superfluous. If the host actually says not ready from
scsi_host_queue_ready()we go to the not ready processing clause inscsi_prep_fn()which actually checks the outstanding on the current device and plugs the queue if there aren't any commands. This is actually more correct behaviour than a blind plug regardless (and it's also done under the queue lock), so I think this is the correct fix.
That looks good, much better than juggling locks there.
Signature · 1 line
Jens Axboe
Quoted 45 lines · James Bottomley
Quoted 37 lines · Jens Axboe
Quoted 28 lines · Elias Oltmanns
Quoted 20 lines · Elias Oltmanns
Quoted 19 lines · James Bottomley
Quoted 11 lines · Jens Axboe
Quoted 6 lines · James Bottomley
Quoted 1 line · Jens Axboe
Yep,
blk_plug_device()needs to be called with the queue lock held.That's what the comment says ... but if you replaced the
test_bitwith an atomic operation then the rest of it does look to be in no need of serialisation ... unless there's something I missed?Indeed, but then you would have to use atomic bitops everywhere and that is the bit we moved away from.
Not necessarily ... only for
QUEUE_FLAG_CLUSTER. That's really only in this one place and then the one inblk_remove_plugwould have to becometest_and_clear_bit. All the other places barringloop_unplug()are only tests (which don't affect the atomicity).Quoted 6 lines
Quoted 3 lines
It's just for SCSI the double spin lock followed by double spin unlock to get the locking right is kind of nasty ... I'm just wondering what the universe would look like if it were rendered unnecessary.
We have to consider one more thing: Without the locking in
blk_plug_device(), the following sequence of events may occur:Quoted 6 lines
Actually, it's worse than that. Locking is required in order to make absolutely sure that the
unplug_timeris active iffQUEUE_FLAG_PLUGGEDis set. Admittedly, it seems *very* unlikely thatblk_remove_plug()will complete before the call tomod_timer()inblk_plug_device()even though it has started only *after* a call totest_and_set_bit(). However, if such a thing would ever happen, it could have dire consequences.Quoted 4 lines
Both are races possible without either atomic bitops or the queue lock being held. We can't properly mix eg
set_bit()and__set_bit(). The plugged bit is the most hammered, so it's staying non-atomic and SCSI will need to provide proper locking there.You're the boss.
Quoted 7 lines
Actually, after all of this, it looks like the host queue plug is superfluous. If the host actually says not ready from
scsi_host_queue_ready()we go to the not ready processing clause inscsi_prep_fn()which actually checks the outstanding on the current device and plugs the queue if there aren't any commands. This is actually more correct behaviour than a blind plug regardless (and it's also done under the queue lock), so I think this is the correct fix.
Indeed, a very neat way out. Will this be queued up for stable too?
Regards,
Elias
James Bottomley
maintainerQuoted 54 lines · Elias Oltmanns
Quoted 45 lines · James Bottomley
Quoted 37 lines · Jens Axboe
Quoted 28 lines · Elias Oltmanns
Quoted 20 lines · Elias Oltmanns
Quoted 19 lines · James Bottomley
Quoted 11 lines · Jens Axboe
Quoted 6 lines · James Bottomley
Quoted 1 line · Jens Axboe
Yep,
blk_plug_device()needs to be called with the queue lock held.That's what the comment says ... but if you replaced the
test_bitwith an atomic operation then the rest of it does look to be in no need of serialisation ... unless there's something I missed?Indeed, but then you would have to use atomic bitops everywhere and that is the bit we moved away from.
Not necessarily ... only for
QUEUE_FLAG_CLUSTER. That's really only in this one place and then the one inblk_remove_plugwould have to becometest_and_clear_bit. All the other places barringloop_unplug()are only tests (which don't affect the atomicity).Quoted 6 lines
Quoted 3 lines
It's just for SCSI the double spin lock followed by double spin unlock to get the locking right is kind of nasty ... I'm just wondering what the universe would look like if it were rendered unnecessary.
We have to consider one more thing: Without the locking in
blk_plug_device(), the following sequence of events may occur:Quoted 6 lines
Actually, it's worse than that. Locking is required in order to make absolutely sure that the
unplug_timeris active iffQUEUE_FLAG_PLUGGEDis set. Admittedly, it seems *very* unlikely thatblk_remove_plug()will complete before the call tomod_timer()inblk_plug_device()even though it has started only *after* a call totest_and_set_bit(). However, if such a thing would ever happen, it could have dire consequences.Quoted 4 lines
Both are races possible without either atomic bitops or the queue lock being held. We can't properly mix eg
set_bit()and__set_bit(). The plugged bit is the most hammered, so it's staying non-atomic and SCSI will need to provide proper locking there.You're the boss.
Quoted 7 lines
Actually, after all of this, it looks like the host queue plug is superfluous. If the host actually says not ready from
scsi_host_queue_ready()we go to the not ready processing clause inscsi_prep_fn()which actually checks the outstanding on the current device and plugs the queue if there aren't any commands. This is actually more correct behaviour than a blind plug regardless (and it's also done under the queue lock), so I think this is the correct fix.
Quoted 1 line
Indeed, a very neat way out. Will this be queued up for stable too?
No ... the fact that there's another plug in the path means that the consequences should be unobservable (borne out by the fact that we have no bug reports). Prudence dictates that there's greater risk from an untested fix than there is from the original problem.
James
James Bottomley
maintainerQuoted 11 lines · Elias Oltmanns
Quoted 10 lines · James Bottomley
Quoted 8 lines · Elias Oltmanns
Hi James,
sorry for bothering you but I've just noticed that the patch below has neither been scheduled for the stable review, nor queued up for Linus. May be you just don't consider this serious enough for these trees but I wanted to make sure that the situation will be dealt with eventualy. The patch applies to 2.6.26-rc8.
Quoted 8 lines
Quoted 3 lines
OK, well at first glance, the locking around
device_blockedandhost_blockedlooks pointless. What are the failure traces you're using to decide they need spinlock protection?
scsi_queue_insert()as well asscsi_finish_command()can be called at any time as part of regular command completion or error handling. There is no reason why the ->request_fn() for the same device or for another device on the same host should not be in progress at the same time.
So would I be correct in deducing you haven't seen an observed failure?
The reason no locks are necessary is that there's no race to mediate. The checks are only is it set or not ... unless we get down to zero depth in which case the decrements are done under lock.
Quoted 7 lines
Quoted 3 lines
The
blk_plug_queuechange looks reasonable ... however,blk_plug_queueitself looks like it might not entirely need the queue lock ... I need to investigate more closely.Well, I rather think it does. We have to serialise access to the
unplug_timerand there is a call to__set_bit()which, as I understand, requires the calling function to ensure atomicity.
It does at the moment ... it just looks like it could make use of test_and_set_bit() to avoid the requirement. The access to the timer uses mod_timer() which is specifically designed not to require serialisation.
James
Quoted 17 lines · James Bottomley
Quoted 16 lines · Elias Oltmanns
Quoted 13 lines · James Bottomley
Quoted 8 lines · Elias Oltmanns
Hi James,
sorry for bothering you but I've just noticed that the patch below has neither been scheduled for the stable review, nor queued up for Linus. May be you just don't consider this serious enough for these trees but I wanted to make sure that the situation will be dealt with eventualy. The patch applies to 2.6.26-rc8.
OK, well at first glance, the locking around
device_blockedandhost_blockedlooks pointless. What are the failure traces you're using to decide they need spinlock protection?
Quoted 6 lines
Quoted 4 lines
scsi_queue_insert()as well asscsi_finish_command()can be called at any time as part of regular command completion or error handling. There is no reason why the ->request_fn() for the same device or for another device on the same host should not be in progress at the same time.So would I be correct in deducing you haven't seen an observed failure?
Yes, I don't even have an SMP machine.
Quoted 3 lines
The reason no locks are necessary is that there's no race to mediate. The checks are only is it set or not ...
I'm not sure whether that is of any consequence. Don't get me wrong, I really don't know and you may well be right. But how exactly does decrementing from 2 to 1 work? Do we know for sure that there will always be at least one bit set so reading that address will reliably return a non zero value?
Quoted 2 lines
unless we get down to zero depth in which case the decrements are done under lock.
Sorry, but this simply doesn't resolve the matter at hand. scsi_finish_command() can change (host|device)_blocked values to zero at any time currently *not* protected by any lock. In much the same way scsi_queue_insert() can change these values from zero to something else at any time.
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
The
blk_plug_queuechange looks reasonable ... however,blk_plug_queueitself looks like it might not entirely need the queue lock ... I need to investigate more closely.
Quoted 8 lines
Quoted 3 lines
Well, I rather think it does. We have to serialise access to the
unplug_timerand there is a call to__set_bit()which, as I understand, requires the calling function to ensure atomicity.It does at the moment ... it just looks like it could make use of
test_and_set_bit()to avoid the requirement. The access to the timer usesmod_timer()which is specifically designed not to require serialisation.
Concurrent calls to mod_timer() are alright; I'm not so sure what happens when del_timer() is called at the same time (haven't checked though, so you might be right here).
Regards,
Elias
Quoted 8 lines · Elias Oltmanns
Quoted 2 lines
The reason no locks are necessary is that there's no race to mediate. The checks are only is it set or not ...
I'm not sure whether that is of any consequence. Don't get me wrong, I really don't know and you may well be right. But how exactly does decrementing from 2 to 1 work? Do we know for sure that there will always be at least one bit set so reading that address will reliably return a non zero value?
The assumption we make (and it is believed to be true on all SMP systems) is that a write to a naturally aligned memory location that is sized <= sizeof(long) is atomic. That is, a reader will get either the previous value or the subsequent value, not a mixture. The RCU code relies heavily on this assumption.
Signature · 4 lines
Intel are signing my paycheques ... these opinions are still mine "Bill, look, we understand that you're interested in selling us this operating system, but compare it to ours. We can't possibly take such a retrograde step."
Quoted 11 lines · Matthew Wilcox
Quoted 9 lines · Elias Oltmanns
Quoted 3 lines
The reason no locks are necessary is that there's no race to mediate.
The checks are only is it set or not ...
I'm not sure whether that is of any consequence. Don't get me wrong, I really don't know and you may well be right. But how exactly does decrementing from 2 to 1 work? Do we know for sure that there will always be at least one bit set so reading that address will reliably return a non zero value?
Quoted 5 lines
The assumption we make (and it is believed to be true on all SMP systems) is that a write to a naturally aligned memory location that is sized <= sizeof(long) is atomic. That is, a reader will get either the previous value or the subsequent value, not a mixture. The RCU code relies heavily on this assumption.
Does that mean that where ever I have
spin_lock_irqsave(some_lock, flags);
var = some_val;spin_unlock_irqrestore(some_lock, flags);
I could just as well discard the locking provided that sizeof(var) <= sizeof(long)
because the assignment of some_val to var will be atomic anyway?
Regards,
Elias
James Bottomley
maintainerQuoted 20 lines · Elias Oltmanns
Quoted 11 lines · Matthew Wilcox
Quoted 9 lines · Elias Oltmanns
Quoted 3 lines
The reason no locks are necessary is that there's no race to mediate.
The checks are only is it set or not ...
I'm not sure whether that is of any consequence. Don't get me wrong, I really don't know and you may well be right. But how exactly does decrementing from 2 to 1 work? Do we know for sure that there will always be at least one bit set so reading that address will reliably return a non zero value?
Quoted 5 lines
The assumption we make (and it is believed to be true on all SMP systems) is that a write to a naturally aligned memory location that is sized <= sizeof(long) is atomic. That is, a reader will get either the previous value or the subsequent value, not a mixture. The RCU code relies heavily on this assumption.
Does that mean that where ever I have
Quoted 7 lines
spin_lock_irqsave(some_lock, flags);
var =some_val;spin_unlock_irqrestore(some_lock, flags);I could just as well discard the locking provided that sizeof(var) <= sizeof(long)
because the assignment ofsome_valto var will be atomic anyway?
I think you're still confused about the difference between integral observation of values and serialisation.
Assignment is always integral. In your example above anything observing the variable always sees either the previous value or the one you set it to, nothing else. So, if all you're doing is setting a flag and clearing it (as in setting it to an integral value, not setting it via a bit operation) and checking it, no locking is needed. It's when you start doing operations on variables that require serialisation, or the variables themselves are absolute indicators of events that need serialisation that you start having to introduce locking.
James
Quoted 2 lines · James Bottomley
Quoted 1 line · Elias Oltmanns
Matthew Wilcox <[email protected]> wrote:
[...]
Quoted 19 lines
Quoted 8 lines
Quoted 5 lines
The assumption we make (and it is believed to be true on all SMP systems) is that a write to a naturally aligned memory location that is sized <= sizeof(long) is atomic. That is, a reader will get either the previous value or the subsequent value, not a mixture. The RCU code relies heavily on this assumption.
Does that mean that where ever I have
Quoted 7 lines
spin_lock_irqsave(some_lock, flags);
var =some_val;spin_unlock_irqrestore(some_lock, flags);I could just as well discard the locking provided that sizeof(var) <= sizeof(long)
because the assignment ofsome_valto var will be atomic anyway?I think you're still confused about the difference between integral observation of values and serialisation.
Quoted 8 lines
Assignment is always integral. In your example above anything observing the variable always sees either the previous value or the one you set it to, nothing else. So, if all you're doing is setting a flag and clearing it (as in setting it to an integral value, not setting it via a bit operation) and checking it, no locking is needed. It's when you start doing operations on variables that require serialisation, or the variables themselves are absolute indicators of events that need serialisation that you start having to introduce locking.
Right. Thank you very much for the clarification.
Regards,
Elias
Quoted 9 lines · Elias Oltmanns
Quoted 5 lines · Matthew Wilcox
The assumption we make (and it is believed to be true on all SMP systems) is that a write to a naturally aligned memory location that is sized <= sizeof(long) is atomic. That is, a reader will get either the previous value or the subsequent value, not a mixture. The RCU code relies heavily on this assumption.
Does that mean that where ever I have
Quoted 7 lines
spin_lock_irqsave(some_lock, flags);
var =some_val;spin_unlock_irqrestore(some_lock, flags);I could just as well discard the locking provided that sizeof(var) <= sizeof(long)
because the assignment ofsome_valto var will be atomic anyway?
You might need a barrier.
Also, I realised I mis-spoke above. Early Alphas do not have instructions that store byte or 16-bit sized quantities. So the compiler implements a byte store by loading a 32-bit quantity, replacing the appropriate 8 bits with the new value and storing the word back again. The same is true for most processors with, say, bit-sized quantities. So while it's atomic with respect to other _readers_, it's unsafe with respect to other _writers_ in that if you have two modification attempts simultaneously, you can end up with only one modification having taken place, not both.
eg:
CPU 0 CPU 1
load value
load value
modify
modify
store value
store value
CPU 1 got the old value, and stored it over CPU 0's value.
CPUs which have instructions like cmpxchg can avoid these kinds of problems, but not all do. It's certainly not something that you can make a blanket statement on; you really need to analyse it carefully.
Signature · 4 lines
Intel are signing my paycheques ... these opinions are still mine "Bill, look, we understand that you're interested in selling us this operating system, but compare it to ours. We can't possibly take such a retrograde step."
James Bottomley
maintainerQuoted 28 lines · Elias Oltmanns
Quoted 17 lines · James Bottomley
Quoted 16 lines · Elias Oltmanns
Quoted 13 lines · James Bottomley
Quoted 8 lines · Elias Oltmanns
Hi James,
sorry for bothering you but I've just noticed that the patch below has neither been scheduled for the stable review, nor queued up for Linus. May be you just don't consider this serious enough for these trees but I wanted to make sure that the situation will be dealt with eventualy. The patch applies to 2.6.26-rc8.
OK, well at first glance, the locking around
device_blockedandhost_blockedlooks pointless. What are the failure traces you're using to decide they need spinlock protection?Quoted 6 lines
Quoted 4 lines
scsi_queue_insert()as well asscsi_finish_command()can be called at any time as part of regular command completion or error handling. There is no reason why the ->request_fn() for the same device or for another device on the same host should not be in progress at the same time.So would I be correct in deducing you haven't seen an observed failure?
Yes, I don't even have an SMP machine.
Quoted 1 line
Quoted 8 lines
Quoted 2 lines
The reason no locks are necessary is that there's no race to mediate. The checks are only is it set or not ...
I'm not sure whether that is of any consequence. Don't get me wrong, I really don't know and you may well be right. But how exactly does decrementing from 2 to 1 work? Do we know for sure that there will always be at least one bit set so reading that address will reliably return a non zero value?
both the check and the decrement are under a lock already. But anyway, for an int, we always read an integral value (that's a guarantee of the CPU architecture)
Quoted 8 lines
Quoted 2 lines
unless we get down to zero depth in which case the decrements are done under lock.
Sorry, but this simply doesn't resolve the matter at hand.
scsi_finish_command()can change (host|device)_blocked values to zero at any time currently *not* protected by any lock. In much the same wayscsi_queue_insert()can change these values from zero to something else at any time.
Look more closely at the requirements for the decrements: There have to be no outstanding commands: nothing can be in scsi_finish_command for the device (or the host for host_blocked). Likewise, scsi_queue_insert() is called either for retry return (but nothing outstanding, so can't) or for queuecommand() failure.
The way locking changes in the queue function introduces a race ... the block check to the queuecommand. Introducing additional locking around the variable setting can't mediate that race, so the code has to be prepared for it (which it is).
Quoted 9 lines
Quoted 9 lines
Quoted 8 lines
Quoted 4 lines
The
blk_plug_queuechange looks reasonable ... however,blk_plug_queueitself looks like it might not entirely need the queue lock ... I need to investigate more closely.Well, I rather think it does. We have to serialise access to the
unplug_timerand there is a call to__set_bit()which, as I understand, requires the calling function to ensure atomicity.
Quoted 8 lines
Quoted 4 lines
It does at the moment ... it just looks like it could make use of
test_and_set_bit()to avoid the requirement. The access to the timer usesmod_timer()which is specifically designed not to require serialisation.Concurrent calls to
mod_timer()are alright; I'm not so sure what happens whendel_timer()is called at the same time (haven't checked though, so you might be right here).
James
Quoted 2 lines · James Bottomley
Quoted 1 line · Elias Oltmanns
James Bottomley <[email protected]> wrote:
[...]
Quoted 9 lines
Quoted 8 lines
Quoted 2 lines
unless we get down to zero depth in which case the decrements are done under lock.
Sorry, but this simply doesn't resolve the matter at hand.
scsi_finish_command()can change (host|device)_blocked values to zero at any time currently *not* protected by any lock. In much the same wayscsi_queue_insert()can change these values from zero to something else at any time.
Quoted 3 lines
Look more closely at the requirements for the decrements: There have to be no outstanding commands: nothing can be in
scsi_finish_commandfor the device (or the host forhost_blocked).
Yes, I agree as far as the decrements are concerned. There still is the check
if (sdev->device_blocked)
which can happen while ->device_blocked is changed either by scsi_finish_command() or scsi_queue_insert(). If I understand Matthew and you correctly, this doesn't pose any problem because assigning an int is an atomic operation anyway.
Thanks for explaining,
Elias