Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for CRIU unprivileged mode #20683

Merged
merged 5 commits into from
Apr 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 51 additions & 2 deletions dev/com.ibm.ws.kernel.boot.ws-server/publish/bin/server
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@
#TODO need to reflect this to criu checkpoint
# Set to 1-4 with 4 being max verbosity. Two "2" is the default.
#
# CRIU_UNPRIVILEGED - Use criu in unprivileged mode.
# Set to one of true, True, TRUE, 1
# or false, False, FALSE, 0 to continue
# to use criu in privileged mode.
#
# CRIU_EXTRA_ARGS - Pass extra arguments to `criu restore`. Extra arguments are
# appended to the end of the list of arguments and can therefore
# override existing arguments, if desired.
#
# PID_DIR - The directory that should be used for server pid file(s).
# The default value is ${WLP_OUTPUT_DIR}/.pid
#
Expand Down Expand Up @@ -296,6 +305,36 @@ if [ -n "${WLP_SKIP_BOOTSTRAP_AGENT}" ]; then
JAVA_AGENT_QUOTED=
fi

##
## Determine if CRIU supports unprivileged mode.
## Determine which CRIU mode to use, based on current euid, whether or not criu supports unprivileged mode, and the value of the CRIU_UNPRIVILEGED env var.
## Set DO_CRIU_UNPRIVILEGED based on the above.
##
checkCriuUnprivileged()
{
if [ -n "$CRIU_UNPRIVILEGED" ]; then
if [ "${CRIU_UNPRIVILEGED}" = "true" -o "${CRIU_UNPRIVILEGED}" = "1" -o "${CRIU_UNPRIVILEGED}" = "TRUE" -o "${CRIU_UNPRIVILEGED}" = "True" ]; then
# Use unprivileged if explicitly requested; if CRIU doesn't support it we'll hit an error later that will be shown to the user
DO_CRIU_UNPRIVILEGED=true
else
DO_CRIU_UNPRIVILEGED=false
fi
return
fi

# Determine if CRIU supports unprivileged mode
criu --help | grep -q -e '--unprivileged' && CRIU_SUPPORTS_UNPRIVILEGED=true || CRIU_SUPPORTS_UNPRIVILEGED=false

# Determine which CRIU mode to use, based on current euid and value of CRIU_SUPPORTS_UNPRIVILEGED
DO_CRIU_UNPRIVILEGED=false
if [ "$(id -u)" != 0 ]; then
# Not root, unprivileged by default if CRIU supports it
if [ $CRIU_SUPPORTS_UNPRIVILEGED = true ]; then
DO_CRIU_UNPRIVILEGED=true
fi
fi
}

##
## createServer: Function to launch server create
##
Expand Down Expand Up @@ -932,6 +971,11 @@ javaCmd()
fi

JVM_OPTIONS_QUOTED="$JVM_OPTIONS_QUOTED -XX:+EnableCRIUSupport $X_WLP_IMMUTABLE_VARS"

checkCriuUnprivileged
if [ $DO_CRIU_UNPRIVILEGED = true ]; then
JVM_OPTIONS_QUOTED="$JVM_OPTIONS_QUOTED -Dio.openliberty.checkpoint.criu.unprivileged=true"
fi
fi


Expand Down Expand Up @@ -1293,13 +1337,18 @@ criuRestore()
CRIU_LOG_LEVEL=2
fi

checkCriuUnprivileged
if [ $DO_CRIU_UNPRIVILEGED = true ]; then
CRIU_EXTRA_ARGS="--unprivileged $CRIU_EXTRA_ARGS"
fi

if $BACKGROUND_RESTORE
then
mkdirs "${X_PID_DIR}"
rmIfExist "${X_PID_FILE}"
criu restore --cpu-cap=none --file-locks --tcp-established --images-dir=${SERVER_OUTPUT_DIR}/workarea/checkpoint/image \
--shell-job --verbosity=${CRIU_LOG_LEVEL} --log-file ${X_LOG_DIR}/checkpoint/restore.log --pidfile ${X_PID_FILE} \
--restore-detached
--restore-detached ${CRIU_EXTRA_ARGS}
rc=$?
PID=`cat ${X_PID_FILE}`
if [ $rc = 0 ]; then
Expand All @@ -1326,7 +1375,7 @@ criuRestore()
trap "killIfRunning $TAIL_PID" EXIT

criu restore --cpu-cap=none --file-locks --tcp-established --images-dir=${SERVER_OUTPUT_DIR}/workarea/checkpoint/image \
--shell-job --verbosity=${CRIU_LOG_LEVEL} --log-file ${X_LOG_DIR}/checkpoint/restore.log
--shell-job --verbosity=${CRIU_LOG_LEVEL} --log-file ${X_LOG_DIR}/checkpoint/restore.log ${CRIU_EXTRA_ARGS}
rc=$?

kill $TAIL_PID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
public class CheckpointImpl implements RuntimeUpdateListener, ServerReadyStatus {

private static final String CHECKPOINT_STUB_CRIU = "io.openliberty.checkpoint.stub.criu";
private static final String CHECKPOINT_CRIU_UNPRIVILEGED = "io.openliberty.checkpoint.criu.unprivileged";
static final String HOOKS_REF_NAME_SINGLE_THREAD = "hooksSingleThread";
static final String HOOKS_REF_NAME_MULTI_THREAD = "hooksMultiThread";
private static final String DIR_CHECKPOINT = "checkpoint/";
Expand Down Expand Up @@ -110,7 +111,7 @@ public CheckpointImpl(ComponentContext cc, @Reference WsLocationAdmin locAdmin,
*/
this.criu = new ExecuteCRIU() {
@Override
public void dump(Runnable prepare, Runnable restore, File imageDir, String logFileName, File workDir, File envProps) throws CheckpointFailedException {
public void dump(Runnable prepare, Runnable restore, File imageDir, String logFileName, File workDir, File envProps, boolean unprivileged) throws CheckpointFailedException {
prepare.run();
restore.run();
}
Expand Down Expand Up @@ -236,14 +237,16 @@ void checkpoint() throws CheckpointFailedException {
debug(tc, () -> "ExecuteCRIU service does not support checkpoint: " + cpfe.getMessage());
throw cpfe;
}
boolean unprivileged = Boolean.valueOf(cc.getBundleContext().getProperty(CHECKPOINT_CRIU_UNPRIVILEGED));
File imageDir = getImageDir();
debug(tc, () -> "criu attempt dump to '" + imageDir + "' and exit process.");

criu.dump(() -> prepare(singleThreadPrepareHooks),
() -> restore(singleThreadRestoreHooks),
imageDir, CHECKPOINT_LOG_FILE,
getLogsCheckpoint(),
getEnvProperties());
getEnvProperties(),
unprivileged);

debug(tc, () -> "criu dumped to " + imageDir + ", now in recovered process.");
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ public interface ExecuteCRIU {
* @param logFileName
* @param workDir
* @param envProps
* @param unprivileged
* @throws CheckpointFailedException
*/
default void dump(Runnable prepare, Runnable restore, File imageDir, String logFileName, File workDir, File envProps) throws CheckpointFailedException {
default void dump(Runnable prepare, Runnable restore, File imageDir, String logFileName, File workDir, File envProps, boolean unprivileged) throws CheckpointFailedException {
// do nothing
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class ExecuteCRIU_OpenJ9 implements ExecuteCRIU {

@Override
@FFDCIgnore({ JVMCheckpointException.class, SystemCheckpointException.class, RestoreException.class, JVMCRIUException.class, RuntimeException.class })
public void dump(Runnable prepare, Runnable restore, File imageDir, String logFileName, File workDir, File envProps) throws CheckpointFailedException {
public void dump(Runnable prepare, Runnable restore, File imageDir, String logFileName, File workDir, File envProps, boolean unprivileged) throws CheckpointFailedException {
CRIUSupport criuSupport = new CRIUSupport(imageDir.toPath());
criuSupport.registerPreSnapshotHook(prepare);
criuSupport.registerPostRestoreHook(restore);
Expand All @@ -38,6 +38,13 @@ public void dump(Runnable prepare, Runnable restore, File imageDir, String logFi
criuSupport.setWorkDir(workDir.toPath());
criuSupport.setTCPEstablished(true);
criuSupport.registerRestoreEnvFile(envProps.toPath());
if (unprivileged) {
try {
criuSupport.setUnprivileged(true);
} catch (NoSuchMethodError e) {
throw new CheckpointFailedException(Type.UNKNOWN, "JVM does not support CRIU unprivileged mode", e);
}
}
try {
criuSupport.checkpointJVM();
} catch (JVMCheckpointException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private static ExecuteCRIU createCRIUNotSupported(CheckpointFailedException.Type
final CheckpointFailedException criuSupportException = new CheckpointFailedException(type, msg, null);
return new ExecuteCRIU() {
@Override
public void dump(Runnable prepare, Runnable restore, File imageDir, String logFileName, File workDir, File envProps) throws CheckpointFailedException {
public void dump(Runnable prepare, Runnable restore, File imageDir, String logFileName, File workDir, File envProps, boolean unprivileged) throws CheckpointFailedException {
throw criuSupportException;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ public TestCRIU() {
}

@Override
public void dump(Runnable prepare, Runnable restore, File imageDir, String logFileName, File workDir, File envProps) throws CheckpointFailedException {
public void dump(Runnable prepare, Runnable restore, File imageDir, String logFileName, File workDir, File envProps,
boolean unprivileged) throws CheckpointFailedException {
singleThreaded.set(true);
try {
prepare.run();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,18 @@ public CRIUSupport setWorkDir(Path workDir) {
return this;
}

/**
* Controls whether CRIU will be invoked in privileged or unprivileged mode.
* <p>
* Default: false
*
* @param unprivileged
* @return this
*/
public CRIUSupport setUnprivileged(boolean unprivileged) {
return this;
}

/**
* Append new environment variables to the set returned by ProcessEnvironment.getenv(...) upon
* restore. All pre-existing (environment variables from checkpoint run) env
Expand Down