2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-09-10 13:42:36 +04:00
/*
* Support for Partition Mobility / Migration
*
* Copyright ( C ) 2010 Nathan Fontenot
* Copyright ( C ) 2010 IBM Corporation
*/
2019-06-27 08:30:43 +03:00
# define pr_fmt(fmt) "mobility: " fmt
2019-06-12 07:45:05 +03:00
# include <linux/cpu.h>
2010-09-10 13:42:36 +04:00
# include <linux/kernel.h>
# include <linux/kobject.h>
powerpc/pseries/mobility: use stop_machine for join/suspend
The partition suspend sequence as specified in the platform
architecture requires that all active processor threads call
H_JOIN, which:
- suspends the calling thread until it is the target of
an H_PROD; or
- immediately returns H_CONTINUE, if the calling thread is the last to
call H_JOIN. This thread is expected to call ibm,suspend-me to
completely suspend the partition.
Upon returning from ibm,suspend-me the calling thread must wake all
others using H_PROD.
rtas_ibm_suspend_me_unsafe() uses on_each_cpu() to implement this
protocol, but because of its synchronizing nature this is susceptible
to deadlock versus users of stop_machine() or other callers of
on_each_cpu().
Not only is stop_machine() intended for use cases like this, it
handles error propagation and allows us to keep the data shared
between CPUs minimal: a single atomic counter which ensures exactly
one CPU will wake the others from their joined states.
Switch the migration code to use stop_machine() and a less complex
local implementation of the H_JOIN/ibm,suspend-me logic, which
carries additional benefits:
- more informative error reporting, appropriately ratelimited
- resets the lockup detector / watchdog on resume to prevent lockup
warnings when the OS has been suspended for a time exceeding the
threshold.
Fixes: 91dc182ca6e2 ("[PATCH] powerpc: special-case ibm,suspend-me RTAS call")
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201207215200.1785968-13-nathanl@linux.ibm.com
2020-12-08 00:51:44 +03:00
# include <linux/nmi.h>
2019-08-02 22:29:26 +03:00
# include <linux/sched.h>
2010-09-10 13:42:36 +04:00
# include <linux/smp.h>
2011-05-27 21:27:45 +04:00
# include <linux/stat.h>
powerpc/pseries/mobility: use stop_machine for join/suspend
The partition suspend sequence as specified in the platform
architecture requires that all active processor threads call
H_JOIN, which:
- suspends the calling thread until it is the target of
an H_PROD; or
- immediately returns H_CONTINUE, if the calling thread is the last to
call H_JOIN. This thread is expected to call ibm,suspend-me to
completely suspend the partition.
Upon returning from ibm,suspend-me the calling thread must wake all
others using H_PROD.
rtas_ibm_suspend_me_unsafe() uses on_each_cpu() to implement this
protocol, but because of its synchronizing nature this is susceptible
to deadlock versus users of stop_machine() or other callers of
on_each_cpu().
Not only is stop_machine() intended for use cases like this, it
handles error propagation and allows us to keep the data shared
between CPUs minimal: a single atomic counter which ensures exactly
one CPU will wake the others from their joined states.
Switch the migration code to use stop_machine() and a less complex
local implementation of the H_JOIN/ibm,suspend-me logic, which
carries additional benefits:
- more informative error reporting, appropriately ratelimited
- resets the lockup detector / watchdog on resume to prevent lockup
warnings when the OS has been suspended for a time exceeding the
threshold.
Fixes: 91dc182ca6e2 ("[PATCH] powerpc: special-case ibm,suspend-me RTAS call")
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201207215200.1785968-13-nathanl@linux.ibm.com
2020-12-08 00:51:44 +03:00
# include <linux/stop_machine.h>
2010-09-10 13:42:36 +04:00
# include <linux/completion.h>
# include <linux/device.h>
# include <linux/delay.h>
# include <linux/slab.h>
2018-07-05 19:24:59 +03:00
# include <linux/stringify.h>
2010-09-10 13:42:36 +04:00
2014-07-16 06:02:43 +04:00
# include <asm/machdep.h>
2010-09-10 13:42:36 +04:00
# include <asm/rtas.h>
# include "pseries.h"
2019-06-12 07:45:06 +03:00
# include "../../kernel/cacheinfo.h"
2010-09-10 13:42:36 +04:00
static struct kobject * mobility_kobj ;
struct update_props_workarea {
2015-03-04 22:59:33 +03:00
__be32 phandle ;
__be32 state ;
__be64 reserved ;
__be32 nprops ;
2013-08-15 09:23:47 +04:00
} __packed ;
2010-09-10 13:42:36 +04:00
# define NODE_ACTION_MASK 0xff000000
# define NODE_COUNT_MASK 0x00ffffff
# define DELETE_DT_NODE 0x01000000
# define UPDATE_DT_NODE 0x02000000
# define ADD_DT_NODE 0x03000000
powerpc/pseries: Expose pseries devicetree_update()
Newer firmware on Power systems can transparently reassign platform resources
(CPU and Memory) in use. For instance, if a processor or memory unit is
predicted to fail, the platform may transparently move the processing to an
equivalent unused processor or the memory state to an equivalent unused
memory unit. However, reassigning resources across NUMA boundaries may alter
the performance of the partition. When such reassignment is necessary, the
Platform Resource Reassignment Notification (PRRN) option provides a
mechanism to inform the Linux kernel of changes to the NUMA affinity of
its platform resources.
When rtasd receives a PRRN event, it needs to make a series of RTAS
calls (ibm,update-nodes and ibm,update-properties) to retrieve the
updated device tree information. These calls are already handled in the
pseries_devicetree_update() routine used in partition migration.
This patch exposes pseries_devicetree_update() to make it accessible
to other pseries routines, this patch also updates pseries_devicetree_update()
to take a 32-bit scope parameter. The scope value, which was previously hard
coded to 1 for partition migration, is used for the RTAS calls
ibm,update-nodes/properties to update the device tree.
Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2013-04-24 09:47:11 +04:00
# define MIGRATION_SCOPE (1)
2017-01-06 22:28:54 +03:00
# define PRRN_SCOPE -2
powerpc/pseries: Expose pseries devicetree_update()
Newer firmware on Power systems can transparently reassign platform resources
(CPU and Memory) in use. For instance, if a processor or memory unit is
predicted to fail, the platform may transparently move the processing to an
equivalent unused processor or the memory state to an equivalent unused
memory unit. However, reassigning resources across NUMA boundaries may alter
the performance of the partition. When such reassignment is necessary, the
Platform Resource Reassignment Notification (PRRN) option provides a
mechanism to inform the Linux kernel of changes to the NUMA affinity of
its platform resources.
When rtasd receives a PRRN event, it needs to make a series of RTAS
calls (ibm,update-nodes and ibm,update-properties) to retrieve the
updated device tree information. These calls are already handled in the
pseries_devicetree_update() routine used in partition migration.
This patch exposes pseries_devicetree_update() to make it accessible
to other pseries routines, this patch also updates pseries_devicetree_update()
to take a 32-bit scope parameter. The scope value, which was previously hard
coded to 1 for partition migration, is used for the RTAS calls
ibm,update-nodes/properties to update the device tree.
Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2013-04-24 09:47:11 +04:00
static int mobility_rtas_call ( int token , char * buf , s32 scope )
2010-09-10 13:42:36 +04:00
{
int rc ;
spin_lock ( & rtas_data_buf_lock ) ;
memcpy ( rtas_data_buf , buf , RTAS_DATA_BUF_SIZE ) ;
powerpc/pseries: Expose pseries devicetree_update()
Newer firmware on Power systems can transparently reassign platform resources
(CPU and Memory) in use. For instance, if a processor or memory unit is
predicted to fail, the platform may transparently move the processing to an
equivalent unused processor or the memory state to an equivalent unused
memory unit. However, reassigning resources across NUMA boundaries may alter
the performance of the partition. When such reassignment is necessary, the
Platform Resource Reassignment Notification (PRRN) option provides a
mechanism to inform the Linux kernel of changes to the NUMA affinity of
its platform resources.
When rtasd receives a PRRN event, it needs to make a series of RTAS
calls (ibm,update-nodes and ibm,update-properties) to retrieve the
updated device tree information. These calls are already handled in the
pseries_devicetree_update() routine used in partition migration.
This patch exposes pseries_devicetree_update() to make it accessible
to other pseries routines, this patch also updates pseries_devicetree_update()
to take a 32-bit scope parameter. The scope value, which was previously hard
coded to 1 for partition migration, is used for the RTAS calls
ibm,update-nodes/properties to update the device tree.
Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2013-04-24 09:47:11 +04:00
rc = rtas_call ( token , 2 , 1 , NULL , rtas_data_buf , scope ) ;
2010-09-10 13:42:36 +04:00
memcpy ( buf , rtas_data_buf , RTAS_DATA_BUF_SIZE ) ;
spin_unlock ( & rtas_data_buf_lock ) ;
return rc ;
}
2020-12-08 00:52:00 +03:00
static int delete_dt_node ( struct device_node * dn )
2010-09-10 13:42:36 +04:00
{
2019-06-27 08:30:44 +03:00
pr_debug ( " removing node %pOFfp \n " , dn ) ;
2010-09-10 13:42:36 +04:00
dlpar_detach_node ( dn ) ;
return 0 ;
}
static int update_dt_property ( struct device_node * dn , struct property * * prop ,
const char * name , u32 vd , char * value )
{
struct property * new_prop = * prop ;
int more = 0 ;
/* A negative 'vd' value indicates that only part of the new property
* value is contained in the buffer and we need to call
* ibm , update - properties again to get the rest of the value .
*
* A negative value is also the two ' s compliment of the actual value .
*/
if ( vd & 0x80000000 ) {
vd = ~ vd + 1 ;
more = 1 ;
}
if ( new_prop ) {
/* partial property fixup */
char * new_data = kzalloc ( new_prop - > length + vd , GFP_KERNEL ) ;
if ( ! new_data )
return - ENOMEM ;
memcpy ( new_data , new_prop - > value , new_prop - > length ) ;
memcpy ( new_data + new_prop - > length , value , vd ) ;
kfree ( new_prop - > value ) ;
new_prop - > value = new_data ;
new_prop - > length + = vd ;
} else {
new_prop = kzalloc ( sizeof ( * new_prop ) , GFP_KERNEL ) ;
if ( ! new_prop )
return - ENOMEM ;
new_prop - > name = kstrdup ( name , GFP_KERNEL ) ;
if ( ! new_prop - > name ) {
kfree ( new_prop ) ;
return - ENOMEM ;
}
new_prop - > length = vd ;
new_prop - > value = kzalloc ( new_prop - > length , GFP_KERNEL ) ;
if ( ! new_prop - > value ) {
kfree ( new_prop - > name ) ;
kfree ( new_prop ) ;
return - ENOMEM ;
}
memcpy ( new_prop - > value , value , vd ) ;
* prop = new_prop ;
}
if ( ! more ) {
2019-06-27 08:30:44 +03:00
pr_debug ( " updating node %pOF property %s \n " , dn , name ) ;
2012-10-02 20:58:46 +04:00
of_update_property ( dn , new_prop ) ;
2013-08-15 09:23:45 +04:00
* prop = NULL ;
2010-09-10 13:42:36 +04:00
}
return 0 ;
}
2020-12-08 00:52:00 +03:00
static int update_dt_node ( struct device_node * dn , s32 scope )
2010-09-10 13:42:36 +04:00
{
struct update_props_workarea * upwa ;
struct property * prop = NULL ;
2013-08-15 09:23:46 +04:00
int i , rc , rtas_rc ;
2010-09-10 13:42:36 +04:00
char * prop_data ;
char * rtas_buf ;
int update_properties_token ;
2015-03-04 22:59:33 +03:00
u32 nprops ;
2013-04-24 09:49:36 +04:00
u32 vd ;
2010-09-10 13:42:36 +04:00
update_properties_token = rtas_token ( " ibm,update-properties " ) ;
if ( update_properties_token = = RTAS_UNKNOWN_SERVICE )
return - EINVAL ;
rtas_buf = kzalloc ( RTAS_DATA_BUF_SIZE , GFP_KERNEL ) ;
if ( ! rtas_buf )
return - ENOMEM ;
upwa = ( struct update_props_workarea * ) & rtas_buf [ 0 ] ;
2020-12-08 00:52:00 +03:00
upwa - > phandle = cpu_to_be32 ( dn - > phandle ) ;
2010-09-10 13:42:36 +04:00
do {
2013-08-15 09:23:46 +04:00
rtas_rc = mobility_rtas_call ( update_properties_token , rtas_buf ,
powerpc/pseries: Expose pseries devicetree_update()
Newer firmware on Power systems can transparently reassign platform resources
(CPU and Memory) in use. For instance, if a processor or memory unit is
predicted to fail, the platform may transparently move the processing to an
equivalent unused processor or the memory state to an equivalent unused
memory unit. However, reassigning resources across NUMA boundaries may alter
the performance of the partition. When such reassignment is necessary, the
Platform Resource Reassignment Notification (PRRN) option provides a
mechanism to inform the Linux kernel of changes to the NUMA affinity of
its platform resources.
When rtasd receives a PRRN event, it needs to make a series of RTAS
calls (ibm,update-nodes and ibm,update-properties) to retrieve the
updated device tree information. These calls are already handled in the
pseries_devicetree_update() routine used in partition migration.
This patch exposes pseries_devicetree_update() to make it accessible
to other pseries routines, this patch also updates pseries_devicetree_update()
to take a 32-bit scope parameter. The scope value, which was previously hard
coded to 1 for partition migration, is used for the RTAS calls
ibm,update-nodes/properties to update the device tree.
Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2013-04-24 09:47:11 +04:00
scope ) ;
2013-08-15 09:23:46 +04:00
if ( rtas_rc < 0 )
2010-09-10 13:42:36 +04:00
break ;
prop_data = rtas_buf + sizeof ( * upwa ) ;
2015-03-04 22:59:33 +03:00
nprops = be32_to_cpu ( upwa - > nprops ) ;
2010-09-10 13:42:36 +04:00
2013-08-15 09:23:48 +04:00
/* On the first call to ibm,update-properties for a node the
* the first property value descriptor contains an empty
* property name , the property value length encoded as u32 ,
* and the property value is the node path being updated .
2013-04-24 09:49:36 +04:00
*/
2013-08-15 09:23:48 +04:00
if ( * prop_data = = 0 ) {
prop_data + + ;
2015-03-04 22:59:33 +03:00
vd = be32_to_cpu ( * ( __be32 * ) prop_data ) ;
2013-08-15 09:23:48 +04:00
prop_data + = vd + sizeof ( vd ) ;
2015-03-04 22:59:33 +03:00
nprops - - ;
2013-08-15 09:23:48 +04:00
}
2013-04-24 09:49:36 +04:00
2015-03-04 22:59:33 +03:00
for ( i = 0 ; i < nprops ; i + + ) {
2010-09-10 13:42:36 +04:00
char * prop_name ;
2013-04-24 09:49:36 +04:00
prop_name = prop_data ;
2010-09-10 13:42:36 +04:00
prop_data + = strlen ( prop_name ) + 1 ;
2015-03-04 22:59:33 +03:00
vd = be32_to_cpu ( * ( __be32 * ) prop_data ) ;
2013-04-24 09:49:36 +04:00
prop_data + = sizeof ( vd ) ;
2010-09-10 13:42:36 +04:00
switch ( vd ) {
case 0x00000000 :
/* name only property, nothing to do */
break ;
case 0x80000000 :
2016-04-28 08:34:55 +03:00
of_remove_property ( dn , of_find_property ( dn ,
prop_name , NULL ) ) ;
2010-09-10 13:42:36 +04:00
prop = NULL ;
break ;
default :
rc = update_dt_property ( dn , & prop , prop_name ,
vd , prop_data ) ;
if ( rc ) {
2020-12-08 00:51:41 +03:00
pr_err ( " updating %s property failed: %d \n " ,
prop_name , rc ) ;
2010-09-10 13:42:36 +04:00
}
prop_data + = vd ;
2020-12-08 00:51:40 +03:00
break ;
2010-09-10 13:42:36 +04:00
}
2019-08-02 22:29:26 +03:00
cond_resched ( ) ;
2010-09-10 13:42:36 +04:00
}
2019-08-02 22:29:26 +03:00
cond_resched ( ) ;
2013-08-15 09:23:46 +04:00
} while ( rtas_rc = = 1 ) ;
2010-09-10 13:42:36 +04:00
kfree ( rtas_buf ) ;
return 0 ;
}
2020-12-08 00:52:00 +03:00
static int add_dt_node ( struct device_node * parent_dn , __be32 drc_index )
2010-09-10 13:42:36 +04:00
{
struct device_node * dn ;
int rc ;
2013-08-15 09:23:50 +04:00
dn = dlpar_configure_connector ( drc_index , parent_dn ) ;
2020-12-08 00:52:00 +03:00
if ( ! dn )
2010-09-10 13:42:36 +04:00
return - ENOENT ;
2017-08-21 18:16:49 +03:00
rc = dlpar_attach_node ( dn , parent_dn ) ;
2010-09-10 13:42:36 +04:00
if ( rc )
dlpar_free_cc_nodes ( dn ) ;
2019-06-27 08:30:44 +03:00
pr_debug ( " added node %pOFfp \n " , dn ) ;
2010-09-10 13:42:36 +04:00
return rc ;
}
powerpc/pseries: Expose pseries devicetree_update()
Newer firmware on Power systems can transparently reassign platform resources
(CPU and Memory) in use. For instance, if a processor or memory unit is
predicted to fail, the platform may transparently move the processing to an
equivalent unused processor or the memory state to an equivalent unused
memory unit. However, reassigning resources across NUMA boundaries may alter
the performance of the partition. When such reassignment is necessary, the
Platform Resource Reassignment Notification (PRRN) option provides a
mechanism to inform the Linux kernel of changes to the NUMA affinity of
its platform resources.
When rtasd receives a PRRN event, it needs to make a series of RTAS
calls (ibm,update-nodes and ibm,update-properties) to retrieve the
updated device tree information. These calls are already handled in the
pseries_devicetree_update() routine used in partition migration.
This patch exposes pseries_devicetree_update() to make it accessible
to other pseries routines, this patch also updates pseries_devicetree_update()
to take a 32-bit scope parameter. The scope value, which was previously hard
coded to 1 for partition migration, is used for the RTAS calls
ibm,update-nodes/properties to update the device tree.
Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2013-04-24 09:47:11 +04:00
int pseries_devicetree_update ( s32 scope )
2010-09-10 13:42:36 +04:00
{
char * rtas_buf ;
2015-03-04 22:59:33 +03:00
__be32 * data ;
2010-09-10 13:42:36 +04:00
int update_nodes_token ;
int rc ;
update_nodes_token = rtas_token ( " ibm,update-nodes " ) ;
if ( update_nodes_token = = RTAS_UNKNOWN_SERVICE )
2020-12-08 00:51:39 +03:00
return 0 ;
2010-09-10 13:42:36 +04:00
rtas_buf = kzalloc ( RTAS_DATA_BUF_SIZE , GFP_KERNEL ) ;
if ( ! rtas_buf )
return - ENOMEM ;
do {
powerpc/pseries: Expose pseries devicetree_update()
Newer firmware on Power systems can transparently reassign platform resources
(CPU and Memory) in use. For instance, if a processor or memory unit is
predicted to fail, the platform may transparently move the processing to an
equivalent unused processor or the memory state to an equivalent unused
memory unit. However, reassigning resources across NUMA boundaries may alter
the performance of the partition. When such reassignment is necessary, the
Platform Resource Reassignment Notification (PRRN) option provides a
mechanism to inform the Linux kernel of changes to the NUMA affinity of
its platform resources.
When rtasd receives a PRRN event, it needs to make a series of RTAS
calls (ibm,update-nodes and ibm,update-properties) to retrieve the
updated device tree information. These calls are already handled in the
pseries_devicetree_update() routine used in partition migration.
This patch exposes pseries_devicetree_update() to make it accessible
to other pseries routines, this patch also updates pseries_devicetree_update()
to take a 32-bit scope parameter. The scope value, which was previously hard
coded to 1 for partition migration, is used for the RTAS calls
ibm,update-nodes/properties to update the device tree.
Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2013-04-24 09:47:11 +04:00
rc = mobility_rtas_call ( update_nodes_token , rtas_buf , scope ) ;
2010-09-10 13:42:36 +04:00
if ( rc & & rc ! = 1 )
break ;
2015-03-04 22:59:33 +03:00
data = ( __be32 * ) rtas_buf + 4 ;
while ( be32_to_cpu ( * data ) & NODE_ACTION_MASK ) {
2010-09-10 13:42:36 +04:00
int i ;
2015-03-04 22:59:33 +03:00
u32 action = be32_to_cpu ( * data ) & NODE_ACTION_MASK ;
u32 node_count = be32_to_cpu ( * data ) & NODE_COUNT_MASK ;
2010-09-10 13:42:36 +04:00
data + + ;
for ( i = 0 ; i < node_count ; i + + ) {
2020-12-08 00:52:00 +03:00
struct device_node * np ;
2015-03-04 22:59:33 +03:00
__be32 phandle = * data + + ;
__be32 drc_index ;
2010-09-10 13:42:36 +04:00
2020-12-08 00:52:00 +03:00
np = of_find_node_by_phandle ( be32_to_cpu ( phandle ) ) ;
if ( ! np ) {
pr_warn ( " Failed lookup: phandle 0x%x for action 0x%x \n " ,
be32_to_cpu ( phandle ) , action ) ;
continue ;
}
2010-09-10 13:42:36 +04:00
switch ( action ) {
case DELETE_DT_NODE :
2020-12-08 00:52:00 +03:00
delete_dt_node ( np ) ;
2010-09-10 13:42:36 +04:00
break ;
case UPDATE_DT_NODE :
2020-12-08 00:52:00 +03:00
update_dt_node ( np , scope ) ;
2010-09-10 13:42:36 +04:00
break ;
case ADD_DT_NODE :
drc_index = * data + + ;
2020-12-08 00:52:00 +03:00
add_dt_node ( np , drc_index ) ;
2010-09-10 13:42:36 +04:00
break ;
}
2019-08-02 22:29:26 +03:00
2020-12-08 00:52:00 +03:00
of_node_put ( np ) ;
2019-08-02 22:29:26 +03:00
cond_resched ( ) ;
2010-09-10 13:42:36 +04:00
}
}
2019-08-02 22:29:26 +03:00
cond_resched ( ) ;
2010-09-10 13:42:36 +04:00
} while ( rc = = 1 ) ;
kfree ( rtas_buf ) ;
return rc ;
}
void post_mobility_fixup ( void )
{
int rc ;
2020-12-08 00:51:42 +03:00
rtas_activate_firmware ( ) ;
2014-02-20 00:56:52 +04:00
2019-06-12 07:45:05 +03:00
/*
* We don ' t want CPUs to go online / offline while the device
* tree is being updated .
*/
cpus_read_lock ( ) ;
2019-06-12 07:45:06 +03:00
/*
* It ' s common for the destination firmware to replace cache
* nodes . Release all of the cacheinfo hierarchy ' s references
* before updating the device tree .
*/
cacheinfo_teardown ( ) ;
2014-02-20 00:56:52 +04:00
rc = pseries_devicetree_update ( MIGRATION_SCOPE ) ;
if ( rc )
2020-12-08 00:51:41 +03:00
pr_err ( " device tree update failed: %d \n " , rc ) ;
2010-09-10 13:42:36 +04:00
2019-06-12 07:45:06 +03:00
cacheinfo_rebuild ( ) ;
2019-06-12 07:45:05 +03:00
cpus_read_unlock ( ) ;
2020-11-17 08:59:16 +03:00
/* Possibly switch to a new L1 flush type */
pseries_setup_security_mitigations ( ) ;
2018-03-15 01:40:42 +03:00
2020-05-25 13:43:07 +03:00
/* Reinitialise system information for hv-24x7 */
read_24x7_sys_info ( ) ;
2010-09-10 13:42:36 +04:00
return ;
}
2020-12-08 00:51:43 +03:00
static int poll_vasi_state ( u64 handle , unsigned long * res )
{
unsigned long retbuf [ PLPAR_HCALL_BUFSIZE ] ;
long hvrc ;
int ret ;
hvrc = plpar_hcall ( H_VASI_STATE , retbuf , handle ) ;
switch ( hvrc ) {
case H_SUCCESS :
ret = 0 ;
* res = retbuf [ 0 ] ;
break ;
case H_PARAMETER :
ret = - EINVAL ;
break ;
case H_FUNCTION :
ret = - EOPNOTSUPP ;
break ;
case H_HARDWARE :
default :
pr_err ( " unexpected H_VASI_STATE result %ld \n " , hvrc ) ;
ret = - EIO ;
break ;
}
return ret ;
}
static int wait_for_vasi_session_suspending ( u64 handle )
{
unsigned long state ;
int ret ;
/*
* Wait for transition from H_VASI_ENABLED to
* H_VASI_SUSPENDING . Treat anything else as an error .
*/
while ( true ) {
ret = poll_vasi_state ( handle , & state ) ;
if ( ret ! = 0 | | state = = H_VASI_SUSPENDING ) {
break ;
} else if ( state = = H_VASI_ENABLED ) {
ssleep ( 1 ) ;
} else {
pr_err ( " unexpected H_VASI_STATE result %lu \n " , state ) ;
ret = - EIO ;
break ;
}
}
/*
* Proceed even if H_VASI_STATE is unavailable . If H_JOIN or
* ibm , suspend - me are also unimplemented , we ' ll recover then .
*/
if ( ret = = - EOPNOTSUPP )
ret = 0 ;
return ret ;
}
powerpc/pseries/mobility: use stop_machine for join/suspend
The partition suspend sequence as specified in the platform
architecture requires that all active processor threads call
H_JOIN, which:
- suspends the calling thread until it is the target of
an H_PROD; or
- immediately returns H_CONTINUE, if the calling thread is the last to
call H_JOIN. This thread is expected to call ibm,suspend-me to
completely suspend the partition.
Upon returning from ibm,suspend-me the calling thread must wake all
others using H_PROD.
rtas_ibm_suspend_me_unsafe() uses on_each_cpu() to implement this
protocol, but because of its synchronizing nature this is susceptible
to deadlock versus users of stop_machine() or other callers of
on_each_cpu().
Not only is stop_machine() intended for use cases like this, it
handles error propagation and allows us to keep the data shared
between CPUs minimal: a single atomic counter which ensures exactly
one CPU will wake the others from their joined states.
Switch the migration code to use stop_machine() and a less complex
local implementation of the H_JOIN/ibm,suspend-me logic, which
carries additional benefits:
- more informative error reporting, appropriately ratelimited
- resets the lockup detector / watchdog on resume to prevent lockup
warnings when the OS has been suspended for a time exceeding the
threshold.
Fixes: 91dc182ca6e2 ("[PATCH] powerpc: special-case ibm,suspend-me RTAS call")
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201207215200.1785968-13-nathanl@linux.ibm.com
2020-12-08 00:51:44 +03:00
static void prod_single ( unsigned int target_cpu )
{
long hvrc ;
int hwid ;
hwid = get_hard_smp_processor_id ( target_cpu ) ;
hvrc = plpar_hcall_norets ( H_PROD , hwid ) ;
if ( hvrc = = H_SUCCESS )
return ;
pr_err_ratelimited ( " H_PROD of CPU %u (hwid %d) error: %ld \n " ,
target_cpu , hwid , hvrc ) ;
}
static void prod_others ( void )
{
unsigned int cpu ;
for_each_online_cpu ( cpu ) {
if ( cpu ! = smp_processor_id ( ) )
prod_single ( cpu ) ;
}
}
static u16 clamp_slb_size ( void )
{
u16 prev = mmu_slb_size ;
slb_set_size ( SLB_MIN_SIZE ) ;
return prev ;
}
static int do_suspend ( void )
{
u16 saved_slb_size ;
int status ;
int ret ;
pr_info ( " calling ibm,suspend-me on CPU %i \n " , smp_processor_id ( ) ) ;
/*
* The destination processor model may have fewer SLB entries
* than the source . We reduce mmu_slb_size to a safe minimum
* before suspending in order to minimize the possibility of
* programming non - existent entries on the destination . If
* suspend fails , we restore it before returning . On success
* the OF reconfig path will update it from the new device
* tree after resuming on the destination .
*/
saved_slb_size = clamp_slb_size ( ) ;
ret = rtas_ibm_suspend_me ( & status ) ;
if ( ret ! = 0 ) {
pr_err ( " ibm,suspend-me error: %d \n " , status ) ;
slb_set_size ( saved_slb_size ) ;
}
return ret ;
}
2021-03-15 11:00:44 +03:00
/**
* struct pseries_suspend_info - State shared between CPUs for join / suspend .
* @ counter : Threads are to increment this upon resuming from suspend
* or if an error is received from H_JOIN . The thread which performs
* the first increment ( i . e . sets it to 1 ) is responsible for
* waking the other threads .
2021-03-15 11:00:45 +03:00
* @ done : False if join / suspend is in progress . True if the operation is
* complete ( successful or not ) .
2021-03-15 11:00:44 +03:00
*/
struct pseries_suspend_info {
atomic_t counter ;
2021-03-15 11:00:45 +03:00
bool done ;
2021-03-15 11:00:44 +03:00
} ;
powerpc/pseries/mobility: use stop_machine for join/suspend
The partition suspend sequence as specified in the platform
architecture requires that all active processor threads call
H_JOIN, which:
- suspends the calling thread until it is the target of
an H_PROD; or
- immediately returns H_CONTINUE, if the calling thread is the last to
call H_JOIN. This thread is expected to call ibm,suspend-me to
completely suspend the partition.
Upon returning from ibm,suspend-me the calling thread must wake all
others using H_PROD.
rtas_ibm_suspend_me_unsafe() uses on_each_cpu() to implement this
protocol, but because of its synchronizing nature this is susceptible
to deadlock versus users of stop_machine() or other callers of
on_each_cpu().
Not only is stop_machine() intended for use cases like this, it
handles error propagation and allows us to keep the data shared
between CPUs minimal: a single atomic counter which ensures exactly
one CPU will wake the others from their joined states.
Switch the migration code to use stop_machine() and a less complex
local implementation of the H_JOIN/ibm,suspend-me logic, which
carries additional benefits:
- more informative error reporting, appropriately ratelimited
- resets the lockup detector / watchdog on resume to prevent lockup
warnings when the OS has been suspended for a time exceeding the
threshold.
Fixes: 91dc182ca6e2 ("[PATCH] powerpc: special-case ibm,suspend-me RTAS call")
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201207215200.1785968-13-nathanl@linux.ibm.com
2020-12-08 00:51:44 +03:00
static int do_join ( void * arg )
{
2021-03-15 11:00:44 +03:00
struct pseries_suspend_info * info = arg ;
atomic_t * counter = & info - > counter ;
powerpc/pseries/mobility: use stop_machine for join/suspend
The partition suspend sequence as specified in the platform
architecture requires that all active processor threads call
H_JOIN, which:
- suspends the calling thread until it is the target of
an H_PROD; or
- immediately returns H_CONTINUE, if the calling thread is the last to
call H_JOIN. This thread is expected to call ibm,suspend-me to
completely suspend the partition.
Upon returning from ibm,suspend-me the calling thread must wake all
others using H_PROD.
rtas_ibm_suspend_me_unsafe() uses on_each_cpu() to implement this
protocol, but because of its synchronizing nature this is susceptible
to deadlock versus users of stop_machine() or other callers of
on_each_cpu().
Not only is stop_machine() intended for use cases like this, it
handles error propagation and allows us to keep the data shared
between CPUs minimal: a single atomic counter which ensures exactly
one CPU will wake the others from their joined states.
Switch the migration code to use stop_machine() and a less complex
local implementation of the H_JOIN/ibm,suspend-me logic, which
carries additional benefits:
- more informative error reporting, appropriately ratelimited
- resets the lockup detector / watchdog on resume to prevent lockup
warnings when the OS has been suspended for a time exceeding the
threshold.
Fixes: 91dc182ca6e2 ("[PATCH] powerpc: special-case ibm,suspend-me RTAS call")
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201207215200.1785968-13-nathanl@linux.ibm.com
2020-12-08 00:51:44 +03:00
long hvrc ;
int ret ;
2021-03-15 11:00:45 +03:00
retry :
powerpc/pseries/mobility: use stop_machine for join/suspend
The partition suspend sequence as specified in the platform
architecture requires that all active processor threads call
H_JOIN, which:
- suspends the calling thread until it is the target of
an H_PROD; or
- immediately returns H_CONTINUE, if the calling thread is the last to
call H_JOIN. This thread is expected to call ibm,suspend-me to
completely suspend the partition.
Upon returning from ibm,suspend-me the calling thread must wake all
others using H_PROD.
rtas_ibm_suspend_me_unsafe() uses on_each_cpu() to implement this
protocol, but because of its synchronizing nature this is susceptible
to deadlock versus users of stop_machine() or other callers of
on_each_cpu().
Not only is stop_machine() intended for use cases like this, it
handles error propagation and allows us to keep the data shared
between CPUs minimal: a single atomic counter which ensures exactly
one CPU will wake the others from their joined states.
Switch the migration code to use stop_machine() and a less complex
local implementation of the H_JOIN/ibm,suspend-me logic, which
carries additional benefits:
- more informative error reporting, appropriately ratelimited
- resets the lockup detector / watchdog on resume to prevent lockup
warnings when the OS has been suspended for a time exceeding the
threshold.
Fixes: 91dc182ca6e2 ("[PATCH] powerpc: special-case ibm,suspend-me RTAS call")
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201207215200.1785968-13-nathanl@linux.ibm.com
2020-12-08 00:51:44 +03:00
/* Must ensure MSR.EE off for H_JOIN. */
hard_irq_disable ( ) ;
hvrc = plpar_hcall_norets ( H_JOIN ) ;
switch ( hvrc ) {
case H_CONTINUE :
/*
* All other CPUs are offline or in H_JOIN . This CPU
* attempts the suspend .
*/
ret = do_suspend ( ) ;
break ;
case H_SUCCESS :
/*
* The suspend is complete and this cpu has received a
2021-03-15 11:00:45 +03:00
* prod , or we ' ve received a stray prod from unrelated
* code ( e . g . paravirt spinlocks ) and we need to join
* again .
*
* This barrier orders the return from H_JOIN above vs
* the load of info - > done . It pairs with the barrier
* in the wakeup / prod path below .
powerpc/pseries/mobility: use stop_machine for join/suspend
The partition suspend sequence as specified in the platform
architecture requires that all active processor threads call
H_JOIN, which:
- suspends the calling thread until it is the target of
an H_PROD; or
- immediately returns H_CONTINUE, if the calling thread is the last to
call H_JOIN. This thread is expected to call ibm,suspend-me to
completely suspend the partition.
Upon returning from ibm,suspend-me the calling thread must wake all
others using H_PROD.
rtas_ibm_suspend_me_unsafe() uses on_each_cpu() to implement this
protocol, but because of its synchronizing nature this is susceptible
to deadlock versus users of stop_machine() or other callers of
on_each_cpu().
Not only is stop_machine() intended for use cases like this, it
handles error propagation and allows us to keep the data shared
between CPUs minimal: a single atomic counter which ensures exactly
one CPU will wake the others from their joined states.
Switch the migration code to use stop_machine() and a less complex
local implementation of the H_JOIN/ibm,suspend-me logic, which
carries additional benefits:
- more informative error reporting, appropriately ratelimited
- resets the lockup detector / watchdog on resume to prevent lockup
warnings when the OS has been suspended for a time exceeding the
threshold.
Fixes: 91dc182ca6e2 ("[PATCH] powerpc: special-case ibm,suspend-me RTAS call")
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201207215200.1785968-13-nathanl@linux.ibm.com
2020-12-08 00:51:44 +03:00
*/
2021-03-15 11:00:45 +03:00
smp_mb ( ) ;
if ( READ_ONCE ( info - > done ) = = false ) {
pr_info_ratelimited ( " premature return from H_JOIN on CPU %i, retrying " ,
smp_processor_id ( ) ) ;
goto retry ;
}
powerpc/pseries/mobility: use stop_machine for join/suspend
The partition suspend sequence as specified in the platform
architecture requires that all active processor threads call
H_JOIN, which:
- suspends the calling thread until it is the target of
an H_PROD; or
- immediately returns H_CONTINUE, if the calling thread is the last to
call H_JOIN. This thread is expected to call ibm,suspend-me to
completely suspend the partition.
Upon returning from ibm,suspend-me the calling thread must wake all
others using H_PROD.
rtas_ibm_suspend_me_unsafe() uses on_each_cpu() to implement this
protocol, but because of its synchronizing nature this is susceptible
to deadlock versus users of stop_machine() or other callers of
on_each_cpu().
Not only is stop_machine() intended for use cases like this, it
handles error propagation and allows us to keep the data shared
between CPUs minimal: a single atomic counter which ensures exactly
one CPU will wake the others from their joined states.
Switch the migration code to use stop_machine() and a less complex
local implementation of the H_JOIN/ibm,suspend-me logic, which
carries additional benefits:
- more informative error reporting, appropriately ratelimited
- resets the lockup detector / watchdog on resume to prevent lockup
warnings when the OS has been suspended for a time exceeding the
threshold.
Fixes: 91dc182ca6e2 ("[PATCH] powerpc: special-case ibm,suspend-me RTAS call")
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201207215200.1785968-13-nathanl@linux.ibm.com
2020-12-08 00:51:44 +03:00
ret = 0 ;
break ;
case H_BAD_MODE :
case H_HARDWARE :
default :
ret = - EIO ;
pr_err_ratelimited ( " H_JOIN error %ld on CPU %i \n " ,
hvrc , smp_processor_id ( ) ) ;
break ;
}
if ( atomic_inc_return ( counter ) = = 1 ) {
pr_info ( " CPU %u waking all threads \n " , smp_processor_id ( ) ) ;
2021-03-15 11:00:45 +03:00
WRITE_ONCE ( info - > done , true ) ;
/*
* This barrier orders the store to info - > done vs subsequent
* H_PRODs to wake the other CPUs . It pairs with the barrier
* in the H_SUCCESS case above .
*/
smp_mb ( ) ;
powerpc/pseries/mobility: use stop_machine for join/suspend
The partition suspend sequence as specified in the platform
architecture requires that all active processor threads call
H_JOIN, which:
- suspends the calling thread until it is the target of
an H_PROD; or
- immediately returns H_CONTINUE, if the calling thread is the last to
call H_JOIN. This thread is expected to call ibm,suspend-me to
completely suspend the partition.
Upon returning from ibm,suspend-me the calling thread must wake all
others using H_PROD.
rtas_ibm_suspend_me_unsafe() uses on_each_cpu() to implement this
protocol, but because of its synchronizing nature this is susceptible
to deadlock versus users of stop_machine() or other callers of
on_each_cpu().
Not only is stop_machine() intended for use cases like this, it
handles error propagation and allows us to keep the data shared
between CPUs minimal: a single atomic counter which ensures exactly
one CPU will wake the others from their joined states.
Switch the migration code to use stop_machine() and a less complex
local implementation of the H_JOIN/ibm,suspend-me logic, which
carries additional benefits:
- more informative error reporting, appropriately ratelimited
- resets the lockup detector / watchdog on resume to prevent lockup
warnings when the OS has been suspended for a time exceeding the
threshold.
Fixes: 91dc182ca6e2 ("[PATCH] powerpc: special-case ibm,suspend-me RTAS call")
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201207215200.1785968-13-nathanl@linux.ibm.com
2020-12-08 00:51:44 +03:00
prod_others ( ) ;
}
/*
* Execution may have been suspended for several seconds , so
* reset the watchdog .
*/
touch_nmi_watchdog ( ) ;
return ret ;
}
2020-12-08 00:51:45 +03:00
/*
* Abort reason code byte 0. We use only the ' Migrating partition ' value .
*/
enum vasi_aborting_entity {
ORCHESTRATOR = 1 ,
VSP_SOURCE = 2 ,
PARTITION_FIRMWARE = 3 ,
PLATFORM_FIRMWARE = 4 ,
VSP_TARGET = 5 ,
MIGRATING_PARTITION = 6 ,
} ;
static void pseries_cancel_migration ( u64 handle , int err )
{
u32 reason_code ;
u32 detail ;
u8 entity ;
long hvrc ;
entity = MIGRATING_PARTITION ;
detail = abs ( err ) & 0xffffff ;
reason_code = ( entity < < 24 ) | detail ;
hvrc = plpar_hcall_norets ( H_VASI_SIGNAL , handle ,
H_VASI_SIGNAL_CANCEL , reason_code ) ;
if ( hvrc )
pr_err ( " H_VASI_SIGNAL error: %ld \n " , hvrc ) ;
}
2020-12-08 00:51:46 +03:00
static int pseries_suspend ( u64 handle )
{
const unsigned int max_attempts = 5 ;
unsigned int retry_interval_ms = 1 ;
unsigned int attempt = 1 ;
int ret ;
while ( true ) {
2021-03-15 11:00:44 +03:00
struct pseries_suspend_info info ;
2020-12-08 00:51:46 +03:00
unsigned long vasi_state ;
int vasi_err ;
2021-03-15 11:00:44 +03:00
info = ( struct pseries_suspend_info ) {
. counter = ATOMIC_INIT ( 0 ) ,
2021-03-15 11:00:45 +03:00
. done = false ,
2021-03-15 11:00:44 +03:00
} ;
ret = stop_machine ( do_join , & info , cpu_online_mask ) ;
2020-12-08 00:51:46 +03:00
if ( ret = = 0 )
break ;
/*
* Encountered an error . If the VASI stream is still
* in Suspending state , it ' s likely a transient
* condition related to some device in the partition
* and we can retry in the hope that the cause has
* cleared after some delay .
*
* A better design would allow drivers etc to prepare
* for the suspend and avoid conditions which prevent
* the suspend from succeeding . For now , we have this
* mitigation .
*/
pr_notice ( " Partition suspend attempt %u of %u error: %d \n " ,
attempt , max_attempts , ret ) ;
if ( attempt = = max_attempts )
break ;
vasi_err = poll_vasi_state ( handle , & vasi_state ) ;
if ( vasi_err = = 0 ) {
if ( vasi_state ! = H_VASI_SUSPENDING ) {
pr_notice ( " VASI state %lu after failed suspend \n " ,
vasi_state ) ;
break ;
}
} else if ( vasi_err ! = - EOPNOTSUPP ) {
pr_err ( " VASI state poll error: %d " , vasi_err ) ;
break ;
}
pr_notice ( " Will retry partition suspend after %u ms \n " ,
retry_interval_ms ) ;
msleep ( retry_interval_ms ) ;
retry_interval_ms * = 10 ;
attempt + + ;
}
return ret ;
}
powerpc/pseries/mobility: use stop_machine for join/suspend
The partition suspend sequence as specified in the platform
architecture requires that all active processor threads call
H_JOIN, which:
- suspends the calling thread until it is the target of
an H_PROD; or
- immediately returns H_CONTINUE, if the calling thread is the last to
call H_JOIN. This thread is expected to call ibm,suspend-me to
completely suspend the partition.
Upon returning from ibm,suspend-me the calling thread must wake all
others using H_PROD.
rtas_ibm_suspend_me_unsafe() uses on_each_cpu() to implement this
protocol, but because of its synchronizing nature this is susceptible
to deadlock versus users of stop_machine() or other callers of
on_each_cpu().
Not only is stop_machine() intended for use cases like this, it
handles error propagation and allows us to keep the data shared
between CPUs minimal: a single atomic counter which ensures exactly
one CPU will wake the others from their joined states.
Switch the migration code to use stop_machine() and a less complex
local implementation of the H_JOIN/ibm,suspend-me logic, which
carries additional benefits:
- more informative error reporting, appropriately ratelimited
- resets the lockup detector / watchdog on resume to prevent lockup
warnings when the OS has been suspended for a time exceeding the
threshold.
Fixes: 91dc182ca6e2 ("[PATCH] powerpc: special-case ibm,suspend-me RTAS call")
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201207215200.1785968-13-nathanl@linux.ibm.com
2020-12-08 00:51:44 +03:00
static int pseries_migrate_partition ( u64 handle )
{
int ret ;
ret = wait_for_vasi_session_suspending ( handle ) ;
if ( ret )
return ret ;
2020-12-08 00:51:46 +03:00
ret = pseries_suspend ( handle ) ;
powerpc/pseries/mobility: use stop_machine for join/suspend
The partition suspend sequence as specified in the platform
architecture requires that all active processor threads call
H_JOIN, which:
- suspends the calling thread until it is the target of
an H_PROD; or
- immediately returns H_CONTINUE, if the calling thread is the last to
call H_JOIN. This thread is expected to call ibm,suspend-me to
completely suspend the partition.
Upon returning from ibm,suspend-me the calling thread must wake all
others using H_PROD.
rtas_ibm_suspend_me_unsafe() uses on_each_cpu() to implement this
protocol, but because of its synchronizing nature this is susceptible
to deadlock versus users of stop_machine() or other callers of
on_each_cpu().
Not only is stop_machine() intended for use cases like this, it
handles error propagation and allows us to keep the data shared
between CPUs minimal: a single atomic counter which ensures exactly
one CPU will wake the others from their joined states.
Switch the migration code to use stop_machine() and a less complex
local implementation of the H_JOIN/ibm,suspend-me logic, which
carries additional benefits:
- more informative error reporting, appropriately ratelimited
- resets the lockup detector / watchdog on resume to prevent lockup
warnings when the OS has been suspended for a time exceeding the
threshold.
Fixes: 91dc182ca6e2 ("[PATCH] powerpc: special-case ibm,suspend-me RTAS call")
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201207215200.1785968-13-nathanl@linux.ibm.com
2020-12-08 00:51:44 +03:00
if ( ret = = 0 )
post_mobility_fixup ( ) ;
2020-12-08 00:51:45 +03:00
else
pseries_cancel_migration ( handle , ret ) ;
powerpc/pseries/mobility: use stop_machine for join/suspend
The partition suspend sequence as specified in the platform
architecture requires that all active processor threads call
H_JOIN, which:
- suspends the calling thread until it is the target of
an H_PROD; or
- immediately returns H_CONTINUE, if the calling thread is the last to
call H_JOIN. This thread is expected to call ibm,suspend-me to
completely suspend the partition.
Upon returning from ibm,suspend-me the calling thread must wake all
others using H_PROD.
rtas_ibm_suspend_me_unsafe() uses on_each_cpu() to implement this
protocol, but because of its synchronizing nature this is susceptible
to deadlock versus users of stop_machine() or other callers of
on_each_cpu().
Not only is stop_machine() intended for use cases like this, it
handles error propagation and allows us to keep the data shared
between CPUs minimal: a single atomic counter which ensures exactly
one CPU will wake the others from their joined states.
Switch the migration code to use stop_machine() and a less complex
local implementation of the H_JOIN/ibm,suspend-me logic, which
carries additional benefits:
- more informative error reporting, appropriately ratelimited
- resets the lockup detector / watchdog on resume to prevent lockup
warnings when the OS has been suspended for a time exceeding the
threshold.
Fixes: 91dc182ca6e2 ("[PATCH] powerpc: special-case ibm,suspend-me RTAS call")
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201207215200.1785968-13-nathanl@linux.ibm.com
2020-12-08 00:51:44 +03:00
return ret ;
}
2020-12-08 00:51:47 +03:00
int rtas_syscall_dispatch_ibm_suspend_me ( u64 handle )
{
return pseries_migrate_partition ( handle ) ;
}
2017-06-06 16:32:03 +03:00
static ssize_t migration_store ( struct class * class ,
struct class_attribute * attr , const char * buf ,
size_t count )
2010-09-10 13:42:36 +04:00
{
u64 streamid ;
int rc ;
2014-08-09 01:24:01 +04:00
rc = kstrtou64 ( buf , 0 , & streamid ) ;
2010-09-10 13:42:36 +04:00
if ( rc )
return rc ;
powerpc/pseries/mobility: use stop_machine for join/suspend
The partition suspend sequence as specified in the platform
architecture requires that all active processor threads call
H_JOIN, which:
- suspends the calling thread until it is the target of
an H_PROD; or
- immediately returns H_CONTINUE, if the calling thread is the last to
call H_JOIN. This thread is expected to call ibm,suspend-me to
completely suspend the partition.
Upon returning from ibm,suspend-me the calling thread must wake all
others using H_PROD.
rtas_ibm_suspend_me_unsafe() uses on_each_cpu() to implement this
protocol, but because of its synchronizing nature this is susceptible
to deadlock versus users of stop_machine() or other callers of
on_each_cpu().
Not only is stop_machine() intended for use cases like this, it
handles error propagation and allows us to keep the data shared
between CPUs minimal: a single atomic counter which ensures exactly
one CPU will wake the others from their joined states.
Switch the migration code to use stop_machine() and a less complex
local implementation of the H_JOIN/ibm,suspend-me logic, which
carries additional benefits:
- more informative error reporting, appropriately ratelimited
- resets the lockup detector / watchdog on resume to prevent lockup
warnings when the OS has been suspended for a time exceeding the
threshold.
Fixes: 91dc182ca6e2 ("[PATCH] powerpc: special-case ibm,suspend-me RTAS call")
Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201207215200.1785968-13-nathanl@linux.ibm.com
2020-12-08 00:51:44 +03:00
rc = pseries_migrate_partition ( streamid ) ;
2020-12-08 00:51:43 +03:00
if ( rc )
return rc ;
2010-09-10 13:42:36 +04:00
return count ;
}
2015-03-05 05:25:38 +03:00
/*
* Used by drmgr to determine the kernel behavior of the migration interface .
*
* Version 1 : Performs all PAPR requirements for migration including
* firmware activation and device tree update .
*/
# define MIGRATION_API_VERSION 1
2017-06-06 16:32:03 +03:00
static CLASS_ATTR_WO ( migration ) ;
2017-01-12 06:54:13 +03:00
static CLASS_ATTR_STRING ( api_version , 0444 , __stringify ( MIGRATION_API_VERSION ) ) ;
2010-09-10 13:42:36 +04:00
static int __init mobility_sysfs_init ( void )
{
int rc ;
mobility_kobj = kobject_create_and_add ( " mobility " , kernel_kobj ) ;
if ( ! mobility_kobj )
return - ENOMEM ;
rc = sysfs_create_file ( mobility_kobj , & class_attr_migration . attr ) ;
2015-03-05 05:25:38 +03:00
if ( rc )
2019-06-27 08:30:43 +03:00
pr_err ( " unable to create migration sysfs file (%d) \n " , rc ) ;
2010-09-10 13:42:36 +04:00
2015-03-05 05:25:38 +03:00
rc = sysfs_create_file ( mobility_kobj , & class_attr_api_version . attr . attr ) ;
if ( rc )
2019-06-27 08:30:43 +03:00
pr_err ( " unable to create api_version sysfs file (%d) \n " , rc ) ;
2015-03-05 05:25:38 +03:00
return 0 ;
2010-09-10 13:42:36 +04:00
}
2014-07-16 06:02:43 +04:00
machine_device_initcall ( pseries , mobility_sysfs_init ) ;