Hi,
This patchset try to solve one deadlock problem which might be caused
by memory allocation with block I/O during runtime PM 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 the deadlock
problem on block device/network device during runtime PM 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 on 'dev_pm_info',
and pm_runtime_set_memalloc_noio(), so that PM Core can teach mm to not
allocate mm with GFP_IO 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.
Andrew, could you queue these patches into your tree since V6 fixes all
your concerns and looks no one objects these patches?
Change logs:
V6:
- fix one compile failure(1/6), and only one line change
V5:
- don't clear GFP_FS
- coding style fix
- add comments
- see details in individual change logs
V4:
- patches from the 2nd to the 6th changed
- call pm_runtime_set_memalloc_noio() after device_add() as pointed
by Alan
- set PF_MEMALLOC_NOIO during runtime_suspend()
V3:
- patch 2/6 and 5/6 changed, see their commit log
- remove RFC from title since several guys have expressed that
it is a reasonable solution
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 | 10 +++++
drivers/base/power/runtime.c | 92 +++++++++++++++++++++++++++++++++++++++++-
drivers/usb/core/hub.c | 13 ++++++
include/linux/pm.h | 1 +
include/linux/pm_runtime.h | 3 ++
include/linux/sched.h | 22 ++++++++++
mm/page_alloc.c | 9 ++++-
mm/vmscan.c | 4 +-
net/core/net-sysfs.c | 5 +++
9 files changed, 154 insertions(+), 5 deletions(-)
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>
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 block device runtime suspend, because runtime resume need
to wait for completion of concurrent runtime suspend.
- 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_flags() 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_NOIO
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>
---
v6:
- replace GFP_IO with __GFP_IO to fix compile failure
v5:
- use inline instead of macro to define memalloc_noio_*
- replace memalloc_noio() with memalloc_noio_flags() to
make code neater
- don't clear GFP_FS because no GFP_IO means
that allocation won't enter device driver as pointed by
Andrew Morton
v4:
- fix comment
v3:
- no change
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 | 22 ++++++++++++++++++++++
mm/page_alloc.c | 9 ++++++++-
mm/vmscan.c | 4 ++--
3 files changed, 32 insertions(+), 3 deletions(-)
@@ -2270,7 +2270,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,{unsignedlongnr_reclaimed;structscan_controlsc={-.gfp_mask=gfp_mask,+.gfp_mask=(gfp_mask=memalloc_noio_flags(gfp_mask)),.may_writepage=!laptop_mode,.nr_to_reclaim=SWAP_CLUSTER_MAX,.may_unmap=1,
@@ -3277,7 +3277,7 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order).may_swap=1,.nr_to_reclaim=max_t(unsignedlong,nr_pages,SWAP_CLUSTER_MAX),-.gfp_mask=gfp_mask,+.gfp_mask=(gfp_mask=memalloc_noio_flags(gfp_mask)),.order=order,.priority=ZONE_RECLAIM_PRIORITY,};
The patch introduces the flag of memalloc_noio in 'struct dev_pm_info'
to help PM core to teach mm not allocating memory with GFP_KERNEL
flag for avoiding probable deadlock.
As explained in the comment, any GFP_KERNEL allocation inside
runtime_resume() or runtime_suspend() 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 in the path recursively.
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: "Rafael J. Wysocki" <redacted>
Signed-off-by: Ming Lei <redacted>
---
v5:
- fix code style error
- add comment on clear the device memalloc_noio flag
v4:
- rename memalloc_noio_resume as memalloc_noio
- remove pm_runtime_get_memalloc_noio()
- add comments on pm_runtime_set_memalloc_noio
v3:
- introduce pm_runtime_get_memalloc_noio()
- hold one global lock on pm_runtime_set_memalloc_noio
- hold device power lock when accessing memalloc_noio_resume
flag suggested by Alan Stern
- implement pm_runtime_set_memalloc_noio without recursion
suggested by Alan Stern
v2:
- introduce pm_runtime_set_memalloc_noio()
---
drivers/base/power/runtime.c | 60 ++++++++++++++++++++++++++++++++++++++++++
include/linux/pm.h | 1 +
include/linux/pm_runtime.h | 3 +++
3 files changed, 64 insertions(+)
@@ -124,6 +124,66 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev)}EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration);+staticintdev_memalloc_noio(structdevice*dev,void*data)+{+returndev->power.memalloc_noio;+}++/*+*pm_runtime_set_memalloc_noio-Setadevice'smemalloc_noioflag.+*@dev:Devicetohandle.+*@enable:TrueforsettingtheflagandFalseforclearingtheflag.+*+*Settheflagforalldevicesinthepathfromthedevicetothe+*rootdeviceinthedevicetreeif@enableistrue,otherwiseclear+*theflagfordevicesinthepathwhosesiblingsdon'tsettheflag.+*+*Thefunctionshouldonlybecalledbyblockdevice,ornetwork+*devicedriverforsolvingthedeadlockproblemduringruntime+*resume/suspend:+*+*IfmemoryallocationwithGFP_KERNELiscalledinsideruntime+*resume/suspendcallbackofanyoneofitsancestors(orthe+*blockdeviceitself),thedeadlockmaybetriggeredinsidethe+*memoryallocationsinceitmightnotcompleteuntiltheblock+*devicebecomesactiveandtheinvoledpageI/Ofinishes.The+*situationispointedoutfirstbyAlanStern.Networkdevice+*areinvolvediniSCSIkindofsituation.+*+*Thelockofdev_hotplug_mutexisheldinthefunctionforhandling+*hotplugracebecausepm_runtime_set_memalloc_noio()maybecalled+*inasyncprobe().+*+*Thefunctionshouldbecalledbetweendevice_add()anddevice_del()+*ontheaffecteddevice(block/networkdevice).+*/+voidpm_runtime_set_memalloc_noio(structdevice*dev,boolenable)+{+staticDEFINE_MUTEX(dev_hotplug_mutex);++mutex_lock(&dev_hotplug_mutex);+for(;;){+/* hold power lock since bitfield is not SMP-safe. */+spin_lock_irq(&dev->power.lock);+dev->power.memalloc_noio=enable;+spin_unlock_irq(&dev->power.lock);++dev=dev->parent;++/*+*clearflagoftheparentdeviceonlyifallthe+*childrendon'tsettheflagbecauseancestor's+*flagwassetbyanyoneofthedescendants.+*/+if(!dev||(!enable&&+device_for_each_child(dev,NULL,+dev_memalloc_noio)))+break;+}+mutex_unlock(&dev_hotplug_mutex);+}+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 and runtime_suspend callback
for block devices and its ancestors.
Cc: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Ming Lei <redacted>
---
v5:
- fix code style and one typo
v4:
- call pm_runtime_set_memalloc_noio(ddev, true) after device_add
---
block/genhd.c | 10 ++++++++++
1 file changed, 10 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 and runtime_suspend callback of network devices in
iSCSI situation, so mark network devices and its ancestor as
'memalloc_noio' 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>
---
v4:
- call pm_runtime_set_memalloc_noio(ddev, true) after
device_add
---
net/core/net-sysfs.c | 5 +++++
1 file changed, 5 insertions(+)
@@ -1421,6 +1424,8 @@ int netdev_register_kobject(struct net_device *net)returnerror;}+pm_runtime_set_memalloc_noio(dev,true);+returnerror;}
--
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/runtime_suspend callback on device with
the flag of 'memalloc_noio' set.
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Oliver Neukum <redacted>
Cc: Rafael J. Wysocki <redacted>
Signed-off-by: Ming Lei <redacted>
---
v5:
- use inline memalloc_noio_save()
v4:
- runtime_suspend need this too because rpm_resume may wait for
completion of concurrent runtime_suspend, so deadlock still may
be triggered in runtime_suspend path.
---
drivers/base/power/runtime.c | 32 ++++++++++++++++++++++++++++++--
1 file changed, 30 insertions(+), 2 deletions(-)
@@ -371,6 +371,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)int(*callback)(structdevice*);structdevice*parent=NULL;intretval;+unsignedintnoio_flag;trace_rpm_suspend(dev,rpmflags);
@@ -480,7 +481,20 @@ static int rpm_suspend(struct device *dev, int rpmflags)if(!callback&&dev->driver&&dev->driver->pm)callback=dev->driver->pm->runtime_suspend;-retval=rpm_callback(callback,dev);+/*+*DeadlockmightbecausedifmemoryallocationwithGFP_KERNEL+*happensinsideruntime_suspendcallbackofoneblockdevice's+*ancestorortheblockdeviceitself.Networkdevicemightbe+*thoughtaspartofiSCSIblockdevice,sonetworkdeviceand+*itsancestorshouldbemarkedasmemalloc_noio.+*/+if(dev->power.memalloc_noio){+noio_flag=memalloc_noio_save();+retval=rpm_callback(callback,dev);+memalloc_noio_restore(noio_flag);+}else{+retval=rpm_callback(callback,dev);+}if(retval)gotofail;
@@ -563,6 +577,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);
@@ -712,7 +727,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.+*/+if(dev->power.memalloc_noio){+noio_flag=memalloc_noio_save();+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>
---
v5:
- use inline memalloc_noio_save()
v4:
- mark current memalloc_noio for every usb device reset
---
drivers/usb/core/hub.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
@@ -5040,6 +5040,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||
@@ -5049,6 +5050,17 @@ int usb_reset_device(struct usb_device *udev)return-EINVAL;}+/*+*Don'tallocatememorywithGFP_KERNELincurrent+*contexttoavoidpossibledeadlockifusbmass+*storageinterfaceorusbnetinterface(iSCSIcase)+*isincludedincurrentconfiguration.Theeasist+*approachistodoitforeverydevicereset,+*becausethedevice'memalloc_noio'flagmayhave+*notbeensetbeforeresetingtheusbdevice.+*/+noio_flag=memalloc_noio_save();+/* Prevent autosuspend during the reset */usb_autoresume_device(udev);
@@ -5093,6 +5105,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: Andrew Morton <akpm@linux-foundation.org> Date: 2012-11-27 20:25:39
On Sat, 24 Nov 2012 20:59:12 +0800
Ming Lei [off-list ref] wrote:
This patchset try to solve one deadlock problem which might be caused
by memory allocation with block I/O during runtime PM 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 the deadlock
problem on block device/network device during runtime PM 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 on 'dev_pm_info',
and pm_runtime_set_memalloc_noio(), so that PM Core can teach mm to not
allocate mm with GFP_IO 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.
Andrew, could you queue these patches into your tree since V6 fixes all
your concerns and looks no one objects these patches?
Yes, this patchset looks ready to run with. But as we're at -rc7 I'll ask
you to refresh, retest and resend after 3.8-rc1, please.
--
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: Rafael J. Wysocki <hidden> Date: 2012-11-27 21:14:48
On Saturday, November 24, 2012 08:59:14 PM Ming Lei wrote:
quoted hunk
The patch introduces the flag of memalloc_noio in 'struct dev_pm_info'
to help PM core to teach mm not allocating memory with GFP_KERNEL
flag for avoiding probable deadlock.
As explained in the comment, any GFP_KERNEL allocation inside
runtime_resume() or runtime_suspend() 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 in the path recursively.
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: "Rafael J. Wysocki" <redacted>
Signed-off-by: Ming Lei <redacted>
---
v5:
- fix code style error
- add comment on clear the device memalloc_noio flag
v4:
- rename memalloc_noio_resume as memalloc_noio
- remove pm_runtime_get_memalloc_noio()
- add comments on pm_runtime_set_memalloc_noio
v3:
- introduce pm_runtime_get_memalloc_noio()
- hold one global lock on pm_runtime_set_memalloc_noio
- hold device power lock when accessing memalloc_noio_resume
flag suggested by Alan Stern
- implement pm_runtime_set_memalloc_noio without recursion
suggested by Alan Stern
v2:
- introduce pm_runtime_set_memalloc_noio()
---
drivers/base/power/runtime.c | 60 ++++++++++++++++++++++++++++++++++++++++++
include/linux/pm.h | 1 +
include/linux/pm_runtime.h | 3 +++
3 files changed, 64 insertions(+)
@@ -124,6 +124,66 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev)}EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration);+staticintdev_memalloc_noio(structdevice*dev,void*data)+{+returndev->power.memalloc_noio;+}++/*+*pm_runtime_set_memalloc_noio-Setadevice'smemalloc_noioflag.+*@dev:Devicetohandle.+*@enable:TrueforsettingtheflagandFalseforclearingtheflag.+*+*Settheflagforalldevicesinthepathfromthedevicetothe+*rootdeviceinthedevicetreeif@enableistrue,otherwiseclear+*theflagfordevicesinthepathwhosesiblingsdon'tsettheflag.+*
Please use counters instead of walking the whole path every time. Ie. in
addition to the flag add a counter to store the number of the device's
children having that flag set.
Besides, don't you need to check children for the arg device itself?
+ * The function should only be called by block device, or network
+ * device driver for solving the deadlock problem during runtime
+ * resume/suspend:
+ *
+ * If memory allocation with GFP_KERNEL is called inside runtime
+ * resume/suspend 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. Network device
+ * are involved in iSCSI kind of situation.
+ *
+ * The lock of dev_hotplug_mutex is held in the function for handling
+ * hotplug race because pm_runtime_set_memalloc_noio() may be called
+ * in async probe().
+ *
+ * The function should be called between device_add() and device_del()
+ * on the affected device(block/network device).
+ */
+void pm_runtime_set_memalloc_noio(struct device *dev, bool enable)
+{
+ static DEFINE_MUTEX(dev_hotplug_mutex);
What's the mutex for?
quoted hunk
+
+ mutex_lock(&dev_hotplug_mutex);
+ for (;;) {
+ /* hold power lock since bitfield is not SMP-safe. */
+ spin_lock_irq(&dev->power.lock);
+ dev->power.memalloc_noio = enable;
+ spin_unlock_irq(&dev->power.lock);
+
+ dev = dev->parent;
+
+ /*
+ * clear flag of the parent device only if all the
+ * children don't set the flag because ancestor's
+ * flag was set by any one of the descendants.
+ */
+ if (!dev || (!enable &&
+ device_for_each_child(dev, NULL,
+ dev_memalloc_noio)))
+ break;
+ }
+ mutex_unlock(&dev_hotplug_mutex);
+}
+EXPORT_SYMBOL_GPL(pm_runtime_set_memalloc_noio);
+
/**
* rpm_check_suspend_allowed - Test whether a device may be suspended.
* @dev: Device to test.
From: Rafael J. Wysocki <hidden> Date: 2012-11-27 21:19:45
On Saturday, November 24, 2012 08:59:17 PM Ming Lei wrote:
quoted hunk
This patch applies the introduced memalloc_noio_save() and
memalloc_noio_restore() to force memory allocation with no I/O
during runtime_resume/runtime_suspend callback on device with
the flag of 'memalloc_noio' set.
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Oliver Neukum <redacted>
Cc: Rafael J. Wysocki <redacted>
Signed-off-by: Ming Lei <redacted>
---
v5:
- use inline memalloc_noio_save()
v4:
- runtime_suspend need this too because rpm_resume may wait for
completion of concurrent runtime_suspend, so deadlock still may
be triggered in runtime_suspend path.
---
drivers/base/power/runtime.c | 32 ++++++++++++++++++++++++++++++--
1 file changed, 30 insertions(+), 2 deletions(-)
@@ -371,6 +371,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)int(*callback)(structdevice*);structdevice*parent=NULL;intretval;+unsignedintnoio_flag;trace_rpm_suspend(dev,rpmflags);
@@ -480,7 +481,20 @@ static int rpm_suspend(struct device *dev, int rpmflags)if(!callback&&dev->driver&&dev->driver->pm)callback=dev->driver->pm->runtime_suspend;-retval=rpm_callback(callback,dev);+/*+*DeadlockmightbecausedifmemoryallocationwithGFP_KERNEL+*happensinsideruntime_suspendcallbackofoneblockdevice's+*ancestorortheblockdeviceitself.Networkdevicemightbe+*thoughtaspartofiSCSIblockdevice,sonetworkdeviceand+*itsancestorshouldbemarkedasmemalloc_noio.+*/+if(dev->power.memalloc_noio){+noio_flag=memalloc_noio_save();+retval=rpm_callback(callback,dev);+memalloc_noio_restore(noio_flag);+}else{+retval=rpm_callback(callback,dev);+}if(retval)gotofail;
@@ -563,6 +577,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);
@@ -712,7 +727,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.+*/+if(dev->power.memalloc_noio){+noio_flag=memalloc_noio_save();+retval=rpm_callback(callback,dev);+memalloc_noio_restore(noio_flag);+}else{+retval=rpm_callback(callback,dev);+}
Please don't duplicate code this way.
You can move that whole thing to rpm_callback(). Yes, you'll probably need to
check dev->power.memalloc_noio twice in there, but that's OK.
if (retval) {
__update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_cancel_pending(dev);
Thanks,
Rafael
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
From: Rafael J. Wysocki <hidden> Date: 2012-11-27 21:41:53
On Tuesday, November 27, 2012 10:19:29 PM Rafael J. Wysocki wrote:
On Saturday, November 24, 2012 08:59:14 PM Ming Lei wrote:
quoted
The patch introduces the flag of memalloc_noio in 'struct dev_pm_info'
to help PM core to teach mm not allocating memory with GFP_KERNEL
flag for avoiding probable deadlock.
As explained in the comment, any GFP_KERNEL allocation inside
runtime_resume() or runtime_suspend() 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 in the path recursively.
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: "Rafael J. Wysocki" <redacted>
Signed-off-by: Ming Lei <redacted>
---
v5:
- fix code style error
- add comment on clear the device memalloc_noio flag
v4:
- rename memalloc_noio_resume as memalloc_noio
- remove pm_runtime_get_memalloc_noio()
- add comments on pm_runtime_set_memalloc_noio
v3:
- introduce pm_runtime_get_memalloc_noio()
- hold one global lock on pm_runtime_set_memalloc_noio
- hold device power lock when accessing memalloc_noio_resume
flag suggested by Alan Stern
- implement pm_runtime_set_memalloc_noio without recursion
suggested by Alan Stern
v2:
- introduce pm_runtime_set_memalloc_noio()
---
drivers/base/power/runtime.c | 60 ++++++++++++++++++++++++++++++++++++++++++
include/linux/pm.h | 1 +
include/linux/pm_runtime.h | 3 +++
3 files changed, 64 insertions(+)
@@ -124,6 +124,66 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev)}EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration);+staticintdev_memalloc_noio(structdevice*dev,void*data)+{+returndev->power.memalloc_noio;+}++/*+*pm_runtime_set_memalloc_noio-Setadevice'smemalloc_noioflag.+*@dev:Devicetohandle.+*@enable:TrueforsettingtheflagandFalseforclearingtheflag.+*+*Settheflagforalldevicesinthepathfromthedevicetothe+*rootdeviceinthedevicetreeif@enableistrue,otherwiseclear+*theflagfordevicesinthepathwhosesiblingsdon'tsettheflag.+*
Please use counters instead of walking the whole path every time. Ie. in
addition to the flag add a counter to store the number of the device's
children having that flag set.
I would use the flag only to store the information that
pm_runtime_set_memalloc_noio(dev, true) has been run for this device directly
and I'd use a counter for everything else.
That is, have power.memalloc_count that would be incremented when (1)
pm_runtime_set_memalloc_noio(dev, true) is called for that device and (2) when
power.memalloc_count for one of its children changes from 0 to 1 (and
analogously for decrementation). Then, check the counter in rpm_callback().
Thanks,
Rafael
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
--
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 Wed, Nov 28, 2012 at 5:24 AM, Rafael J. Wysocki [off-list ref] wrote:
Please don't duplicate code this way.
You can move that whole thing to rpm_callback(). Yes, you'll probably need to
check dev->power.memalloc_noio twice in there, but that's OK.
Good idea, I will update it in v7.
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 Wed, Nov 28, 2012 at 5:19 AM, Rafael J. Wysocki [off-list ref] wrote:
On Saturday, November 24, 2012 08:59:14 PM Ming Lei wrote:
quoted
The patch introduces the flag of memalloc_noio in 'struct dev_pm_info'
to help PM core to teach mm not allocating memory with GFP_KERNEL
flag for avoiding probable deadlock.
As explained in the comment, any GFP_KERNEL allocation inside
runtime_resume() or runtime_suspend() 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 in the path recursively.
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: "Rafael J. Wysocki" <redacted>
Signed-off-by: Ming Lei <redacted>
---
v5:
- fix code style error
- add comment on clear the device memalloc_noio flag
v4:
- rename memalloc_noio_resume as memalloc_noio
- remove pm_runtime_get_memalloc_noio()
- add comments on pm_runtime_set_memalloc_noio
v3:
- introduce pm_runtime_get_memalloc_noio()
- hold one global lock on pm_runtime_set_memalloc_noio
- hold device power lock when accessing memalloc_noio_resume
flag suggested by Alan Stern
- implement pm_runtime_set_memalloc_noio without recursion
suggested by Alan Stern
v2:
- introduce pm_runtime_set_memalloc_noio()
---
drivers/base/power/runtime.c | 60 ++++++++++++++++++++++++++++++++++++++++++
include/linux/pm.h | 1 +
include/linux/pm_runtime.h | 3 +++
3 files changed, 64 insertions(+)
@@ -124,6 +124,66 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev)}EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration);+staticintdev_memalloc_noio(structdevice*dev,void*data)+{+returndev->power.memalloc_noio;+}++/*+*pm_runtime_set_memalloc_noio-Setadevice'smemalloc_noioflag.+*@dev:Devicetohandle.+*@enable:TrueforsettingtheflagandFalseforclearingtheflag.+*+*Settheflagforalldevicesinthepathfromthedevicetothe+*rootdeviceinthedevicetreeif@enableistrue,otherwiseclear+*theflagfordevicesinthepathwhosesiblingsdon'tsettheflag.+*
Please use counters instead of walking the whole path every time. Ie. in
addition to the flag add a counter to store the number of the device's
children having that flag set.
Thanks for your review.
IMO, pm_runtime_set_memalloc_noio() is only called in
probe() and release() of block device and network device, which is
in a very infrequent path, so I am wondering if it is worthy of introducing
another counter for all devices.
Also looks the current implementation of pm_runtime_set_memalloc_noio()
is simple and clean enough with the flag, IMO.
I would use the flag only to store the information that
pm_runtime_set_memalloc_noio(dev, true) has been run for this device directly
and I'd use a counter for everything else.
That is, have power.memalloc_count that would be incremented when (1)
pm_runtime_set_memalloc_noio(dev, true) is called for that device and (2) when
power.memalloc_count for one of its children changes from 0 to 1 (and
analogously for decrementation). Then, check the counter in rpm_callback().
Sorry, could you explain in a bit detail why we need the counter? Looks only
checking the flag in rpm_callback() is enough, doesn't it?
Besides, don't you need to check children for the arg device itself?
It isn't needed since the children of network/block device can't be
involved of the deadlock in runtime PM path.
Also, the function is only called by network device or block device
subsystem, both the two kind of device are class device and should
have no children.
quoted
+ * The function should only be called by block device, or network
+ * device driver for solving the deadlock problem during runtime
+ * resume/suspend:
+ *
+ * If memory allocation with GFP_KERNEL is called inside runtime
+ * resume/suspend 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. Network device
+ * are involved in iSCSI kind of situation.
+ *
+ * The lock of dev_hotplug_mutex is held in the function for handling
+ * hotplug race because pm_runtime_set_memalloc_noio() may be called
+ * in async probe().
+ *
+ * The function should be called between device_add() and device_del()
+ * on the affected device(block/network device).
+ */
+void pm_runtime_set_memalloc_noio(struct device *dev, bool enable)
+{
+ static DEFINE_MUTEX(dev_hotplug_mutex);
What's the mutex for?
It is for avoiding hotplug race, for example, without the mutex,
another child may set the flag between the time device_for_each_child()
runs and the next loop iteration in pm_runtime_set_memalloc_noio(false).
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 Wed, Nov 28, 2012 at 5:19 AM, Rafael J. Wysocki [off-list ref] wrote:
Please use counters instead of walking the whole path every time. Ie. in
addition to the flag add a counter to store the number of the device's
children having that flag set.
Even though counter is added, walking the whole path can't be avoided too,
and may be a explicit walking or recursion, because pm_runtime_set_memalloc_noio
is required to set or clear the flag(or increase/decrease the counter) of
devices in the whole path.
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: Rafael J. Wysocki <hidden> Date: 2012-11-28 09:25:02
On Wednesday, November 28, 2012 12:34:36 PM Ming Lei wrote:
On Wed, Nov 28, 2012 at 5:19 AM, Rafael J. Wysocki [off-list ref] wrote:
quoted
Please use counters instead of walking the whole path every time. Ie. in
addition to the flag add a counter to store the number of the device's
children having that flag set.
Even though counter is added, walking the whole path can't be avoided too,
and may be a explicit walking or recursion, because pm_runtime_set_memalloc_noio
is required to set or clear the flag(or increase/decrease the counter) of
devices in the whole path.
But it doesn't have to walk the children. Moreover, with counters it only
needs to walk the whole path if all devices in it need to be updated. For
example, if you call pm_runtime_set_memalloc_noio(dev, true) for a device
whose parent's counter is greater than zero already, you don't need to
walk the path above the parent.
Thanks,
Rafael
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
--
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 Wed, Nov 28, 2012 at 5:29 PM, Rafael J. Wysocki [off-list ref] wrote:
But it doesn't have to walk the children. Moreover, with counters it only
Yeah, I got it, it is the advantage of counter, but with extra 'int'
field introduced
in 'struct device'.
needs to walk the whole path if all devices in it need to be updated. For
example, if you call pm_runtime_set_memalloc_noio(dev, true) for a device
whose parent's counter is greater than zero already, you don't need to
walk the path above the parent.
We still can do it with the flag only, pm_runtime_set_memalloc_noio(dev, true)
can return immediately if one parent or the 'dev' flag is true.
But considered that the pm_runtime_set_memalloc_noio(dev, false) is only
called in a very infrequent path(network/block device->remove()), looks the
introduced cost isn't worthy of the obtained advantage.
So could you accept not introducing counter? and I will update with the
above improvement you suggested.
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: Rafael J. Wysocki <hidden> Date: 2012-11-28 10:02:17
On Wednesday, November 28, 2012 11:57:19 AM Ming Lei wrote:
On Wed, Nov 28, 2012 at 5:19 AM, Rafael J. Wysocki [off-list ref] wrote:
quoted
On Saturday, November 24, 2012 08:59:14 PM Ming Lei wrote:
quoted
The patch introduces the flag of memalloc_noio in 'struct dev_pm_info'
to help PM core to teach mm not allocating memory with GFP_KERNEL
flag for avoiding probable deadlock.
As explained in the comment, any GFP_KERNEL allocation inside
runtime_resume() or runtime_suspend() 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 in the path recursively.
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: "Rafael J. Wysocki" <redacted>
Signed-off-by: Ming Lei <redacted>
---
v5:
- fix code style error
- add comment on clear the device memalloc_noio flag
v4:
- rename memalloc_noio_resume as memalloc_noio
- remove pm_runtime_get_memalloc_noio()
- add comments on pm_runtime_set_memalloc_noio
v3:
- introduce pm_runtime_get_memalloc_noio()
- hold one global lock on pm_runtime_set_memalloc_noio
- hold device power lock when accessing memalloc_noio_resume
flag suggested by Alan Stern
- implement pm_runtime_set_memalloc_noio without recursion
suggested by Alan Stern
v2:
- introduce pm_runtime_set_memalloc_noio()
---
drivers/base/power/runtime.c | 60 ++++++++++++++++++++++++++++++++++++++++++
include/linux/pm.h | 1 +
include/linux/pm_runtime.h | 3 +++
3 files changed, 64 insertions(+)
@@ -124,6 +124,66 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev)}EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration);+staticintdev_memalloc_noio(structdevice*dev,void*data)+{+returndev->power.memalloc_noio;+}++/*+*pm_runtime_set_memalloc_noio-Setadevice'smemalloc_noioflag.+*@dev:Devicetohandle.+*@enable:TrueforsettingtheflagandFalseforclearingtheflag.+*+*Settheflagforalldevicesinthepathfromthedevicetothe+*rootdeviceinthedevicetreeif@enableistrue,otherwiseclear+*theflagfordevicesinthepathwhosesiblingsdon'tsettheflag.+*
Please use counters instead of walking the whole path every time. Ie. in
addition to the flag add a counter to store the number of the device's
children having that flag set.
Thanks for your review.
IMO, pm_runtime_set_memalloc_noio() is only called in
probe() and release() of block device and network device, which is
in a very infrequent path, so I am wondering if it is worthy of introducing
another counter for all devices.
Well, it may be unfrequent, but does it mean it has to do things that may
be avoided (ie. walking the children of every node in the path in some cases)?
I don't really think that the counters would cost us that much anyway.
Also looks the current implementation of pm_runtime_set_memalloc_noio()
is simple and clean enough with the flag, IMO.
I know you always know better. :-)
quoted
I would use the flag only to store the information that
pm_runtime_set_memalloc_noio(dev, true) has been run for this device directly
and I'd use a counter for everything else.
That is, have power.memalloc_count that would be incremented when (1)
pm_runtime_set_memalloc_noio(dev, true) is called for that device and (2) when
power.memalloc_count for one of its children changes from 0 to 1 (and
analogously for decrementation). Then, check the counter in rpm_callback().
Sorry, could you explain in a bit detail why we need the counter? Looks only
checking the flag in rpm_callback() is enough, doesn't it?
Why would I want to use power.memalloc_count in addition to the
power.memalloc_noio flag?
Consider this:
pm_runtime_set_memalloc_noio(dev):
return if power.memalloc_noio is set
set power.memalloc_noio
loop:
increment power.memalloc_count
if power.memalloc_count is 1 now switch to parent and go to loop
pm_runtime_clear_memalloc_noio(dev):
return if power.memalloc_noio is unset
unset power.memalloc_noio
loop:
decrement power.memalloc_count
if power.memalloc_count is 0 now switch to parent and go to loop
Looks kind of simpler, doesn't it?
And why rpm_callback() should check power.memalloc_count instead of the count?
Because power.memalloc_noio will only be set for devices that
pm_runtime_set_memalloc_noio(dev) was called for directly (not necessarily for
the parents).
And that works even if someone calls any of them twice in a row for the same
device (presumably by mistake) and doesn't have to make any assumptions
about devices it is called for.
quoted
Besides, don't you need to check children for the arg device itself?
It isn't needed since the children of network/block device can't be
involved of the deadlock in runtime PM path.
Also, the function is only called by network device or block device
subsystem, both the two kind of device are class device and should
have no children.
OK, so not walking the arg device's children is an optimization related to
some assumptions regarding who's supposed to use this routine. That should
be clearly documented.
However, I'd prefer it not to make such assumptions in the first place.
quoted
quoted
+ * The function should only be called by block device, or network
+ * device driver for solving the deadlock problem during runtime
+ * resume/suspend:
+ *
+ * If memory allocation with GFP_KERNEL is called inside runtime
+ * resume/suspend 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. Network device
+ * are involved in iSCSI kind of situation.
+ *
+ * The lock of dev_hotplug_mutex is held in the function for handling
+ * hotplug race because pm_runtime_set_memalloc_noio() may be called
+ * in async probe().
+ *
+ * The function should be called between device_add() and device_del()
+ * on the affected device(block/network device).
+ */
+void pm_runtime_set_memalloc_noio(struct device *dev, bool enable)
+{
+ static DEFINE_MUTEX(dev_hotplug_mutex);
What's the mutex for?
It is for avoiding hotplug race, for example, without the mutex,
another child may set the flag between the time device_for_each_child()
runs and the next loop iteration in pm_runtime_set_memalloc_noio(false).
OK
Thanks,
Rafael
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
From: Rafael J. Wysocki <hidden> Date: 2012-11-28 10:10:23
On Wednesday, November 28, 2012 05:47:18 PM Ming Lei wrote:
On Wed, Nov 28, 2012 at 5:29 PM, Rafael J. Wysocki [off-list ref] wrote:
quoted
But it doesn't have to walk the children. Moreover, with counters it only
Yeah, I got it, it is the advantage of counter, but with extra 'int'
field introduced
in 'struct device'.
quoted
needs to walk the whole path if all devices in it need to be updated. For
example, if you call pm_runtime_set_memalloc_noio(dev, true) for a device
whose parent's counter is greater than zero already, you don't need to
walk the path above the parent.
We still can do it with the flag only, pm_runtime_set_memalloc_noio(dev, true)
can return immediately if one parent or the 'dev' flag is true.
But considered that the pm_runtime_set_memalloc_noio(dev, false) is only
called in a very infrequent path(network/block device->remove()), looks the
introduced cost isn't worthy of the obtained advantage.
So could you accept not introducing counter? and I will update with the
above improvement you suggested.
Well, please see my other message I sent a while ago. :-)
Thanks,
Rafael
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
On Wed, Nov 28, 2012 at 6:06 PM, Rafael J. Wysocki [off-list ref] wrote:
Well, it may be unfrequent, but does it mean it has to do things that may
be avoided (ie. walking the children of every node in the path in some cases)?
I agree so without introducing extra cost, :-)
I don't really think that the counters would cost us that much anyway.
On ARM v7, sizeof(struct device) becomes 376 from 368 after introducing
'unsigned int noio_cnt;' to 'struct dev_pm_info', and total memory
increases about 3752bytes in a small configuration(about 494 device instance).
The actual memory increase should be more than the data because 'struct device'
is generally embedded into other concrete device structure.
quoted
Also looks the current implementation of pm_runtime_set_memalloc_noio()
is simple and clean enough with the flag, IMO.
I know you always know better. :-)
We still need to consider cost and the function calling frequency, :-)
quoted
quoted
I would use the flag only to store the information that
pm_runtime_set_memalloc_noio(dev, true) has been run for this device directly
and I'd use a counter for everything else.
That is, have power.memalloc_count that would be incremented when (1)
pm_runtime_set_memalloc_noio(dev, true) is called for that device and (2) when
power.memalloc_count for one of its children changes from 0 to 1 (and
analogously for decrementation). Then, check the counter in rpm_callback().
Sorry, could you explain in a bit detail why we need the counter? Looks only
checking the flag in rpm_callback() is enough, doesn't it?
Why would I want to use power.memalloc_count in addition to the
power.memalloc_noio flag?
Consider this:
pm_runtime_set_memalloc_noio(dev):
return if power.memalloc_noio is set
set power.memalloc_noio
loop:
increment power.memalloc_count
if power.memalloc_count is 1 now switch to parent and go to loop
I am wondering if the above should be changed to below because the child
count of memalloc_noio device need to be recorded.
pm_runtime_set_memalloc_noio(dev):
return if power.memalloc_noio is set
set power.memalloc_noio
loop:
increment power.memalloc_count
switch to parent and go to loop
So pm_runtime_set_memalloc_noio(dev) will become worse than
the improved pm_runtime_set_memalloc_noio(dev, true), which
can return immediately if one dev or parent's flag is true.
pm_runtime_clear_memalloc_noio(dev):
return if power.memalloc_noio is unset
unset power.memalloc_noio
loop:
decrement power.memalloc_count
if power.memalloc_count is 0 now switch to parent and go to loop
The above will perform well than pm_runtime_set_memalloc_noio(dev, false),
because the above avoids to walk children of device.
So one becomes worse and another becomes better, :-)
Also the children count of one device is generally very small, less than
10 for most devices, see the data obtained in one common x86 pc(thinkpad
t410) from below link:
http://kernel.ubuntu.com/~ming/up/t410-dev-child-cnt.log
- about 8 devices whose child count is more than 10, top three are 18, 17 ,12,
and all the three are root devices.
- about 117 devices whose child count is between 1 and 9
- other 501 devices whose child count is zero
From above data, walking device children should have not much effect on
performance of pm_runtime_set_memalloc_noio(), which is also called in
very infrequent path.
Looks kind of simpler, doesn't it?
Looks simpler, but more code lines than single
pm_runtime_set_memalloc_noio(), :-)
And why rpm_callback() should check power.memalloc_count instead of the count?
Because power.memalloc_noio will only be set for devices that
pm_runtime_set_memalloc_noio(dev) was called for directly (not necessarily for
the parents).
And that works even if someone calls any of them twice in a row for the same
device (presumably by mistake) and doesn't have to make any assumptions
about devices it is called for.
IMO, we can ignore the mistake usage because the function is called only
in network/block core code currently, not by individual driver.
quoted
quoted
Besides, don't you need to check children for the arg device itself?
It isn't needed since the children of network/block device can't be
involved of the deadlock in runtime PM path.
Also, the function is only called by network device or block device
subsystem, both the two kind of device are class device and should
have no children.
OK, so not walking the arg device's children is an optimization related to
some assumptions regarding who's supposed to use this routine. That should
be clearly documented.
I think the patch already documents it in the comment of
pm_runtime_set_memalloc_noio().
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>