Hi,
This patchset try to solve one deadlock problem which might be caused
by memory allocation with block I/O during runtime resume and block device
error handling path. Traditionly, the problem is addressed by passing
GFP_NOIO statically to mm, but that is not a effective solution, see
detailed description in patch 1's commit log.
This patch set introduces one process flag and trys to fix one deadlock
problem on block device/network device during runtime resume or usb bus reset.
The 1st one is the change on include/sched.h and mm.
The 2nd patch introduces the flag of memalloc_noio_resume on 'dev_pm_info',
and pm_runtime_set_memalloc_noio(), so that PM Core can teach mm to not
allocate mm with GFP_IOFS during the runtime_resume callback only on
device with the flag set.
The following 2 patches apply the introduced pm_runtime_set_memalloc_noio()
to mark all devices as memalloc_noio_resume in the path from the block or
network device to the root device in device tree.
The last 2 patches are applied again PM and USB subsystem to demonstrate
how to use the introduced mechanism to fix the deadlock problem.
V2:
- remove changes on 'may_writepage' and 'may_swap'(1/6)
- unset GFP_IOFS in try_to_free_pages() path(1/6)
- introduce pm_runtime_set_memalloc_noio()
- only apply the meachnism on block/network device and its ancestors
for runtime resume context
V1:
- take Minchan's change to avoid the check in alloc_page hot path
- change the helpers' style into save/restore as suggested by Alan
- memory allocation with no io in usb bus reset path for all devices
as suggested by Greg and Oliver
block/genhd.c | 8 +++++
drivers/base/power/runtime.c | 69 +++++++++++++++++++++++++++++++++++++++++-
drivers/usb/core/hub.c | 11 +++++++
include/linux/pm.h | 1 +
include/linux/pm_runtime.h | 3 ++
include/linux/sched.h | 10 ++++++
mm/page_alloc.c | 10 +++++-
mm/vmscan.c | 12 ++++++++
net/core/net-sysfs.c | 5 +++
9 files changed, 127 insertions(+), 2 deletions(-)
Thanks,
--
Ming Lei
This patch introduces PF_MEMALLOC_NOIO on process flag('flags' field of
'struct task_struct'), so that the flag can be set by one task
to avoid doing I/O inside memory allocation in the task's context.
The patch trys to solve one deadlock problem caused by block device,
and the problem may happen at least in the below situations:
- during block device runtime resume, if memory allocation with
GFP_KERNEL is called inside runtime resume callback of any one
of its ancestors(or the block device itself), the deadlock may be
triggered inside the memory allocation since it might not complete
until the block device becomes active and the involed page I/O finishes.
The situation is pointed out first by Alan Stern. It is not a good
approach to convert all GFP_KERNEL[1] in the path into GFP_NOIO because
several subsystems may be involved(for example, PCI, USB and SCSI may
be involved for usb mass stoarage device, network devices involved too
in the iSCSI case)
- during error handling of usb mass storage deivce, USB bus reset
will be put on the device, so there shouldn't have any
memory allocation with GFP_KERNEL during USB bus reset, otherwise
the deadlock similar with above may be triggered. Unfortunately, any
usb device may include one mass storage interface in theory, so it
requires all usb interface drivers to handle the situation. In fact,
most usb drivers don't know how to handle bus reset on the device
and don't provide .pre_set() and .post_reset() callback at all, so
USB core has to unbind and bind driver for these devices. So it
is still not practical to resort to GFP_NOIO for solving the problem.
Also the introduced solution can be used by block subsystem or block
drivers too, for example, set the PF_MEMALLOC_NOIO flag before doing
actual I/O transfer.
It is not a good idea to convert all these GFP_KERNEL in the
affected path into GFP_NOIO because these functions doing that may be
implemented as library and will be called in many other contexts.
In fact, memalloc_noio() can convert some of current static GFP_NOIO
allocation into GFP_KERNEL back in other non-affected contexts, at least
almost all GFP_NOIO in USB subsystem can be converted into GFP_KERNEL
after applying the approach and make allocation with GFP_IO
only happen in runtime resume/bus reset/block I/O transfer contexts
generally.
[1], several GFP_KERNEL allocation examples in runtime resume path
- pci subsystem
acpi_os_allocate
<-acpi_ut_allocate
<-ACPI_ALLOCATE_ZEROED
<-acpi_evaluate_object
<-__acpi_bus_set_power
<-acpi_bus_set_power
<-acpi_pci_set_power_state
<-platform_pci_set_power_state
<-pci_platform_power_transition
<-__pci_complete_power_transition
<-pci_set_power_state
<-pci_restore_standard_config
<-pci_pm_runtime_resume
- usb subsystem
usb_get_status
<-finish_port_resume
<-usb_port_resume
<-generic_resume
<-usb_resume_device
<-usb_resume_both
<-usb_runtime_resume
- some individual usb drivers
usblp, uvc, gspca, most of dvb-usb-v2 media drivers, cpia2, az6007, ....
That is just what I have found. Unfortunately, this allocation can
only be found by human being now, and there should be many not found
since any function in the resume path(call tree) may allocate memory
with GFP_KERNEL.
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Oliver Neukum <redacted>
Cc: Jiri Kosina <redacted>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Mel Gorman <redacted>
Cc: KAMEZAWA Hiroyuki <redacted>
Cc: Michal Hocko <redacted>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Rafael J. Wysocki" <redacted>
Signed-off-by: Minchan Kim <minchan@kernel.org>
Signed-off-by: Ming Lei <redacted>
---
v2:
- remove changes on 'may_writepage' and 'may_swap' because that
isn't related with the patchset, and can't introduce I/O in
allocation path if GFP_IOFS is unset, so handing 'may_swap'
and may_writepage on GFP_NOIO or GFP_NOFS should be a
mm internal thing, and let mm guys deal with that, :-).
Looks clearing the two may_XXX flag only excludes dirty pages and
anon pages for relaiming, and the behaviour should be decided
by GFP FLAG, IMO.
- unset GFP_IOFS in try_to_free_pages() path since alloc_page_buffers()
and dma_alloc_from_contiguous may drop into the path, as
pointed by KAMEZAWA Hiroyuki
v1:
- take Minchan's change to avoid the check in alloc_page hot path
- change the helpers' style into save/restore as suggested by Alan
Stern
---
include/linux/sched.h | 10 ++++++++++
mm/page_alloc.c | 10 +++++++++-
mm/vmscan.c | 12 ++++++++++++
3 files changed, 31 insertions(+), 1 deletion(-)
@@ -2298,6 +2298,12 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,.gfp_mask=sc.gfp_mask,};+if(unlikely(memalloc_noio())){+gfp_mask&=~GFP_IOFS;+sc.gfp_mask=gfp_mask;+shrink.gfp_mask=sc.gfp_mask;+}+throttle_direct_reclaim(gfp_mask,zonelist,nodemask);/*
@@ -3298,6 +3304,12 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order)};unsignedlongnr_slab_pages0,nr_slab_pages1;+if(unlikely(memalloc_noio())){+gfp_mask&=~GFP_IOFS;+sc.gfp_mask=gfp_mask;+shrink.gfp_mask=sc.gfp_mask;+}+cond_resched();/**WeneedtobeabletoallocatefromthereservesforRECLAIM_SWAP
--
1.7.9.5
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
The patch introduces the flag of memalloc_noio_resume in
'struct dev_pm_info' to help PM core to teach mm not allocating
memory with GFP_KERNEL flag for avoiding probable deadlock
problem.
As explained in the comment, any GFP_KERNEL allocation inside
runtime_resume on any one of device in the path from one block
or network device to the root device in the device tree may cause
deadlock, the introduced pm_runtime_set_memalloc_noio() sets or
clears the flag on device of the path recursively.
Cc: "Rafael J. Wysocki" <redacted>
Signed-off-by: Ming Lei <redacted>
---
drivers/base/power/runtime.c | 53 ++++++++++++++++++++++++++++++++++++++++++
include/linux/pm.h | 1 +
include/linux/pm_runtime.h | 3 +++
3 files changed, 57 insertions(+)
@@ -124,6 +124,59 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev)}EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration);+staticintdev_memalloc_noio(structdevice*dev,void*data)+{+if(dev->power.memalloc_noio_resume)+return1;+return0;+}++/*+*pm_runtime_set_memalloc_noio-Setadevice'smemalloc_noioflag.+*@dev:Devicetohandle.+*@enable:TrueforsettingtheflagandFalseforclearingtheflag.+*+*Settheflagforalldevicesinthepathfromthedevicetothe+*rootdeviceinthedevicetreeif@enableistrue,otherwiseclear+*theflagfordevicesinthepathwhichsibliingsdon'tsettheflag.+*+*Thefunctionshouldonlybecalledbyblockdevice,ornetwork+*devicedriverforsolvingthedeadlockproblemduringruntime+*resume:+*ifmemoryallocationwithGFP_KERNELiscalledinsideruntime+*resumecallbackofanyoneofitsancestors(ortheblockdevice+*itself),thedeadlockmaybetriggeredinsidethememory+*allocationsinceitmightnotcompleteuntiltheblockdevice+*becomesactiveandtheinvoledpageI/Ofinishes.Thesituation+*ispointedoutfirstbyAlanStern.Networkdeviceareinvolved+*iniSCSIkindofsituation.+*+*Nolockisprovovidedinthefunctionforhandlinghotplograce+*becausepm_runtime_set_memalloc_noio(false)iscalledinparent's+*removepath.+*/+voidpm_runtime_set_memalloc_noio(structdevice*dev,boolenable)+{+dev->power.memalloc_noio_resume=enable;++if(!dev->parent)+return;++if(enable){+pm_runtime_set_memalloc_noio(dev->parent,1);+}else{+/* only clear the flag for one device if all+*childrenofthedevicedon'tsettheflag.+*/+if(device_for_each_child(dev->parent,NULL,+dev_memalloc_noio))+return;++pm_runtime_set_memalloc_noio(dev->parent,0);+}+}+EXPORT_SYMBOL_GPL(pm_runtime_set_memalloc_noio);+/***rpm_check_suspend_allowed-Testwhetheradevicemaybesuspended.*@dev:Devicetotest.
--
1.7.9.5
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
This patch applyes the introduced pm_runtime_set_memalloc_noio on
block device so that PM core will teach mm to not allocate memory with
GFP_IOFS when calling the runtime_resume callback for block devices.
Cc: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Ming Lei <redacted>
---
block/genhd.c | 8 ++++++++
1 file changed, 8 insertions(+)
--
1.7.9.5
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Deadlock might be caused by allocating memory with GFP_KERNEL in
runtime_resume callback of network devices in iSCSI situation, so
mark network devices and its ancestor as 'memalloc_noio_resume'
with the introduced pm_runtime_set_memalloc_noio().
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <redacted>
Cc: David Decotigny <redacted>
Cc: Tom Herbert <redacted>
Cc: Ingo Molnar <redacted>
Signed-off-by: Ming Lei <redacted>
---
net/core/net-sysfs.c | 5 +++++
1 file changed, 5 insertions(+)
--
1.7.9.5
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
This patch applies the introduced memalloc_noio_save() and
memalloc_noio_restore() to force memory allocation with no I/O
during runtime_resume callback on device which is marked as
memalloc_noio_resume.
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Oliver Neukum <redacted>
Cc: Rafael J. Wysocki <redacted>
Signed-off-by: Ming Lei <redacted>
---
drivers/base/power/runtime.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
@@ -556,6 +556,7 @@ static int rpm_resume(struct device *dev, int rpmflags)int(*callback)(structdevice*);structdevice*parent=NULL;intretval=0;+unsignedintnoio_flag;trace_rpm_resume(dev,rpmflags);
@@ -705,7 +706,20 @@ static int rpm_resume(struct device *dev, int rpmflags)if(!callback&&dev->driver&&dev->driver->pm)callback=dev->driver->pm->runtime_resume;-retval=rpm_callback(callback,dev);+/*+*DeadlockmightbecausedifmemoryallocationwithGFP_KERNEL+*happensinsideruntime_resumecallbackofoneblockdevice's+*ancestorortheblockdeviceitself.Networkdevicemightbe+*thoughtaspartofiSCSIblockdevice,sonetworkdeviceand+*itsancestorshouldbemarkedasmemalloc_noio_resume.+*/+if(dev->power.memalloc_noio_resume){+memalloc_noio_save(noio_flag);+retval=rpm_callback(callback,dev);+memalloc_noio_restore(noio_flag);+}else{+retval=rpm_callback(callback,dev);+}if(retval){__update_runtime_status(dev,RPM_SUSPENDED);pm_runtime_cancel_pending(dev);
--
1.7.9.5
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
If one storage interface or usb network interface(iSCSI case)
exists in current configuration, memory allocation with
GFP_KERNEL during usb_device_reset() might trigger I/O transfer
on the storage interface itself and cause deadlock because
the 'us->dev_mutex' is held in .pre_reset() and the storage
interface can't do I/O transfer when the reset is triggered
by other interface, or the error handling can't be completed
if the reset is triggered by the storage itself(error handling path).
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Oliver Neukum <redacted>
Signed-off-by: Ming Lei <redacted>
---
drivers/usb/core/hub.c | 11 +++++++++++
1 file changed, 11 insertions(+)
@@ -5038,6 +5038,7 @@ int usb_reset_device(struct usb_device *udev){intret;inti;+unsignedintnoio_flag;structusb_host_config*config=udev->actconfig;if(udev->state==USB_STATE_NOTATTACHED||
@@ -5047,6 +5048,15 @@ int usb_reset_device(struct usb_device *udev)return-EINVAL;}+/*+*Don'tallocatememorywithGFP_KERNELincurrent+*contexttoavoidpossibledeadlockifusbmass+*storageinterfaceorusbnetinterface(iSCSIcase)+*isincludedincurrentconfiguration.Theeasiest+*approachistodoitforalldevices.+*/+memalloc_noio_save(noio_flag);+/* Prevent autosuspend during the reset */usb_autoresume_device(udev);
@@ -5091,6 +5101,7 @@ int usb_reset_device(struct usb_device *udev)}usb_autosuspend_device(udev);+memalloc_noio_restore(noio_flag);returnret;}EXPORT_SYMBOL_GPL(usb_reset_device);
--
1.7.9.5
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Alan Stern <stern@rowland.harvard.edu> Date: 2012-10-22 14:33:17
On Mon, 22 Oct 2012, Ming Lei wrote:
quoted hunk
+void pm_runtime_set_memalloc_noio(struct device *dev, bool enable)+{+ dev->power.memalloc_noio_resume = enable;++ if (!dev->parent)+ return;++ if (enable) {+ pm_runtime_set_memalloc_noio(dev->parent, 1);+ } else {+ /* only clear the flag for one device if all+ * children of the device don't set the flag.+ */+ if (device_for_each_child(dev->parent, NULL,+ dev_memalloc_noio))+ return;++ pm_runtime_set_memalloc_noio(dev->parent, 0);+ }+}+EXPORT_SYMBOL_GPL(pm_runtime_set_memalloc_noio);
Tail recursion should be implemented as a loop, not as an explicit
recursion. That is, the function should be:
void pm_runtime_set_memalloc_noio(struct device *dev, bool enable)
{
do {
dev->power.memalloc_noio_resume = enable;
if (!enable) {
/*
* Don't clear the parent's flag if any of the
* parent's children have their flag set.
*/
if (device_for_each_child(dev->parent, NULL,
dev_memalloc_noio))
return;
}
dev = dev->parent;
} while (dev);
}
except that you need to add locking, for two reasons:
There's a race. What happens if another child sets the flag
between the time device_for_each_child() runs and the next loop
iteration?
Even without a race, access to bitfields is not SMP-safe
without locking.
Alan Stern
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Alan Stern <stern@rowland.harvard.edu> Date: 2012-10-22 14:37:12
On Mon, 22 Oct 2012, Ming Lei wrote:
quoted hunk
If one storage interface or usb network interface(iSCSI case)
exists in current configuration, memory allocation with
GFP_KERNEL during usb_device_reset() might trigger I/O transfer
on the storage interface itself and cause deadlock because
the 'us->dev_mutex' is held in .pre_reset() and the storage
interface can't do I/O transfer when the reset is triggered
by other interface, or the error handling can't be completed
if the reset is triggered by the storage itself(error handling path).
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Oliver Neukum <redacted>
Signed-off-by: Ming Lei <redacted>
---
drivers/usb/core/hub.c | 11 +++++++++++
1 file changed, 11 insertions(+)
From: Alan Stern <stern@rowland.harvard.edu> Date: 2012-10-22 19:18:32
On Mon, 22 Oct 2012, Ming Lei wrote:
Deadlock might be caused by allocating memory with GFP_KERNEL in
runtime_resume callback of network devices in iSCSI situation, so
mark network devices and its ancestor as 'memalloc_noio_resume'
with the introduced pm_runtime_set_memalloc_noio().
Is this really needed? Even with iSCSI, doesn't register_disk() have
to be called for the underlying block device? And given your 3/6
patch, wouldn't that mark the network device?
Alan Stern
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Tue, Oct 23, 2012 at 3:18 AM, Alan Stern [off-list ref] wrote:
On Mon, 22 Oct 2012, Ming Lei wrote:
Is this really needed? Even with iSCSI, doesn't register_disk() have
to be called for the underlying block device? And given your 3/6
patch, wouldn't that mark the network device?
The problem is that network device is not one ancestor of the
iSCSI disk device, which transfers data over tcp stack.
Thanks,
--
Ming Lei
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Mon, Oct 22, 2012 at 10:37 PM, Alan Stern [off-list ref] wrote:
On Mon, 22 Oct 2012, Ming Lei wrote:
quoted
+ /*+ * Don't allocate memory with GFP_KERNEL in current+ * context to avoid possible deadlock if usb mass+ * storage interface or usbnet interface(iSCSI case)+ * is included in current configuration. The easiest+ * approach is to do it for all devices.+ */+ memalloc_noio_save(noio_flag);
Why not check dev->power.memalloc_noio_resume here too?
Yes, we can use the flag here too even though it is introduced
for rutime_resume case.
Thanks,
--
Ming Lei
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Mon, Oct 22, 2012 at 10:33 PM, Alan Stern [off-list ref] wrote:
Tail recursion should be implemented as a loop, not as an explicit
recursion. That is, the function should be:
void pm_runtime_set_memalloc_noio(struct device *dev, bool enable)
{
do {
dev->power.memalloc_noio_resume = enable;
if (!enable) {
/*
* Don't clear the parent's flag if any of the
* parent's children have their flag set.
*/
if (device_for_each_child(dev->parent, NULL,
dev_memalloc_noio))
return;
}
dev = dev->parent;
} while (dev);
}
OK, will take the non-recursion implementation for saving kernel
stack space.
except that you need to add locking, for two reasons:
There's a race. What happens if another child sets the flag
between the time device_for_each_child() runs and the next loop
iteration?
Yes, I know the race, and not adding a lock because the function
is mostly called in .probe() or .remove() callback and its parent's device
lock is held to avoid this race.
Considered that it may be called in async probe() (scsi disk), one lock
is needed, the simplest way is to add a global lock. Any suggestion?
Even without a race, access to bitfields is not SMP-safe
without locking.
You mean one ancestor device might not be in active when
one of its descendants is being probed or removed?
Thanks,
--
Ming Lei
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Alan Stern <stern@rowland.harvard.edu> Date: 2012-10-23 14:46:52
On Tue, 23 Oct 2012, Ming Lei wrote:
On Mon, Oct 22, 2012 at 10:33 PM, Alan Stern [off-list ref] wrote:
quoted
Tail recursion should be implemented as a loop, not as an explicit
recursion. That is, the function should be:
void pm_runtime_set_memalloc_noio(struct device *dev, bool enable)
{
do {
dev->power.memalloc_noio_resume = enable;
if (!enable) {
/*
* Don't clear the parent's flag if any of the
* parent's children have their flag set.
*/
if (device_for_each_child(dev->parent, NULL,
dev_memalloc_noio))
return;
}
dev = dev->parent;
} while (dev);
}
OK, will take the non-recursion implementation for saving kernel
stack space.
quoted
except that you need to add locking, for two reasons:
There's a race. What happens if another child sets the flag
between the time device_for_each_child() runs and the next loop
iteration?
Yes, I know the race, and not adding a lock because the function
is mostly called in .probe() or .remove() callback and its parent's device
lock is held to avoid this race.
Considered that it may be called in async probe() (scsi disk), one lock
is needed, the simplest way is to add a global lock. Any suggestion?
No. Because of where you put the new flag, it must be protected by
dev->power.lock. And this means the iterative implementation shown
above can't be used as is. It will have to be more like this:
void pm_runtime_set_memalloc_noio(struct device *dev, bool enable)
{
spin_lock_irq(&dev->power.lock);
dev->power.memalloc_noio_resume = enable;
while (dev->parent) {
spin_unlock_irq(&dev->power.lock);
dev = dev->parent;
spin_lock_irq(&dev->power.lock);
/*
* Don't clear the parent's flag if any of the
* parent's children have their flag set.
*/
if (!enable && device_for_each_child(dev->parent, NULL,
dev_memalloc_noio))
break;
dev->power.memalloc_noio_resume = enable;
}
spin_unlock_irq(&dev->power.lock);
}
quoted
Even without a race, access to bitfields is not SMP-safe
without locking.
You mean one ancestor device might not be in active when
one of its descendants is being probed or removed?
No. Consider this example:
struct foo {
int a:1;
int b:1;
} x;
Consider what happens if CPU 0 does "x.a = 1" at the same time as
another CPU 1 does "x.b = 1". The compiler might produce object code
looking like this for CPU 0:
move x, reg1
or 0x1, reg1
move reg1, x
and this for CPU 1:
move x, reg2
or 0x2, reg2
move reg2, x
With no locking, the two "or" instructions could execute
simultaneously. What will the final value of x be?
The two CPUs will interfere, even though they are touching different
bitfields.
Alan Stern
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Tue, Oct 23, 2012 at 10:46 PM, Alan Stern [off-list ref] wrote:
On Tue, 23 Oct 2012, Ming Lei wrote:
quoted
On Mon, Oct 22, 2012 at 10:33 PM, Alan Stern [off-list ref] wrote:
quoted
Tail recursion should be implemented as a loop, not as an explicit
recursion. That is, the function should be:
void pm_runtime_set_memalloc_noio(struct device *dev, bool enable)
{
do {
dev->power.memalloc_noio_resume = enable;
if (!enable) {
/*
* Don't clear the parent's flag if any of the
* parent's children have their flag set.
*/
if (device_for_each_child(dev->parent, NULL,
dev_memalloc_noio))
return;
}
dev = dev->parent;
} while (dev);
}
OK, will take the non-recursion implementation for saving kernel
stack space.
quoted
except that you need to add locking, for two reasons:
There's a race. What happens if another child sets the flag
between the time device_for_each_child() runs and the next loop
iteration?
Yes, I know the race, and not adding a lock because the function
is mostly called in .probe() or .remove() callback and its parent's device
lock is held to avoid this race.
Considered that it may be called in async probe() (scsi disk), one lock
is needed, the simplest way is to add a global lock. Any suggestion?
No. Because of where you put the new flag, it must be protected by
dev->power.lock. And this means the iterative implementation shown
above can't be used as is. It will have to be more like this:
void pm_runtime_set_memalloc_noio(struct device *dev, bool enable)
{
spin_lock_irq(&dev->power.lock);
dev->power.memalloc_noio_resume = enable;
while (dev->parent) {
spin_unlock_irq(&dev->power.lock);
dev = dev->parent;
spin_lock_irq(&dev->power.lock);
/*
* Don't clear the parent's flag if any of the
* parent's children have their flag set.
*/
if (!enable && device_for_each_child(dev->parent, NULL,
With the problem of non-SMP-safe bitfields access, the power.lock should
be held, but that is not enough to prevent children from being probed or
disconnected. Looks another lock is still needed. I think a global lock
is OK in the infrequent path.
quoted
quoted
Even without a race, access to bitfields is not SMP-safe
without locking.
You mean one ancestor device might not be in active when
one of its descendants is being probed or removed?
No. Consider this example:
struct foo {
int a:1;
int b:1;
} x;
Consider what happens if CPU 0 does "x.a = 1" at the same time as
another CPU 1 does "x.b = 1". The compiler might produce object code
looking like this for CPU 0:
move x, reg1
or 0x1, reg1
move reg1, x
and this for CPU 1:
move x, reg2
or 0x2, reg2
move reg2, x
With no locking, the two "or" instructions could execute
simultaneously. What will the final value of x be?
The two CPUs will interfere, even though they are touching different
bitfields.
Got it, thanks for your detailed explanation.
Looks the problem is worse than above, not only bitfields are affected, the
adjacent fields might be involved too, see:
http://lwn.net/Articles/478657/
Thanks,
--
Ming Lei
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Alan Stern <stern@rowland.harvard.edu> Date: 2012-10-23 18:16:23
On Tue, 23 Oct 2012, Ming Lei wrote:
With the problem of non-SMP-safe bitfields access, the power.lock should
be held, but that is not enough to prevent children from being probed or
disconnected. Looks another lock is still needed. I think a global lock
is OK in the infrequent path.
Agreed.
Got it, thanks for your detailed explanation.
Looks the problem is worse than above, not only bitfields are affected, the
adjacent fields might be involved too, see:
http://lwn.net/Articles/478657/
Linus made it clear (in various emails at the time) that the kernel
requires the compiler not to do the sort of things discussed in that
article. But even the restrictions he wanted would not prevent
adjacent bitfields from interfering with each other.
Alan Stern
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: David Laight <hidden> Date: 2012-10-24 09:13:48
Looks the problem is worse than above, not only bitfields are affected, the
adjacent fields might be involved too, see:
http://lwn.net/Articles/478657/
Not mentioned in there is that even with x86/amd64 given
a struct with the following adjacent fields:
char a;
char b;
char c;
then foo->b |= 0x80; might do a 32bit RMW cycle.
This will (well might - but probably does) happen
if compiled to a 'BTS' instruction.
The x86 instruction set docs are actually unclear
as to whether the 32bit cycle might even be misaligned!
amd64 might do a 64bit cycle (not checked the docs).
David
On Wed, 24 Oct 2012 10:06:36 +0100
"David Laight" [off-list ref] wrote:
quoted
Looks the problem is worse than above, not only bitfields are affected, the
adjacent fields might be involved too, see:
http://lwn.net/Articles/478657/
Not mentioned in there is that even with x86/amd64 given
a struct with the following adjacent fields:
char a;
char b;
char c;
then foo->b |= 0x80; might do a 32bit RMW cycle.
There are processors that will do this for the char case at least as they
do byte ops by a mix of 32bit ops and rotate.
This will (well might - but probably does) happen
if compiled to a 'BTS' instruction.
The x86 instruction set docs are actually unclear
as to whether the 32bit cycle might even be misaligned!
amd64 might do a 64bit cycle (not checked the docs).
Even with a suitably aligned field the compiler is at liberty to generate
things like
reg = 0x80
reg |= foo->b
foo->b = reg;
One reason it's a good idea to use set_bit/test_bit and friends.
Alan
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>