Title: [203342] trunk/Source
Revision
203342
Author
[email protected]
Date
2016-07-18 02:04:10 -0700 (Mon, 18 Jul 2016)

Log Message

MemoryPressureHandler doesn't work if cgroups aren't present in Linux
https://bugs.webkit.org/show_bug.cgi?id=155255

Reviewed by Sergio Villar Senin.

Source/WebCore:

Allow to pass an eventFD file descriptor to the MemoryPressureHandler to be monitorized in case cgroups are not
available.

* platform/MemoryPressureHandler.h:
* platform/linux/MemoryPressureHandlerLinux.cpp:

Source/WebKit2:

There's no way to get notifications about memory pressure in Linux without using cgroups that doesn't require a
manual polling. We can get that information from /proc/meminfo, but that's not pollable so it requires to
manually check its contents in a loop sleeping for a while between checks. This means we would be waking up the
process on every poll iteration, most of the times for nothing. That's specially problematic on devices running
on battery. And taking into account that there's a memory pressure handler in every secondary process (Web,
Network and Plugin), we would be waking up all those process all the time. However, not having a memory pressure
handler is even more problematic than the manual polling.
This patch adds a class MemoryPressureMonitor to the manual polling of /proc/meminfo, but runs in the UI
process, to avoid the weakups in all other secondary processes, and uses an eventFD to notify all other
processes. It's only used in case cgroups is not available. The eventFD descriptor is sent to all other
processes at startup, and passed to the MemoryPressureHandler before install() is called for the first
time. To minimize the wakeups even in the UI process, the poll interval is calculated from 1 to 5 seconds
depending on the current memory used, so in case of low memory level we sleep for a longer time.
It's also important to make the memory calculations as accurate as possible to avoid cleaning resources in the
secondary processes unnecessarily.

* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::initializeNetworkProcess): Pass the memory pressure monitor file descriptor to the MemoryPressureHandler.
* NetworkProcess/NetworkProcess.h:
* NetworkProcess/NetworkProcessCreationParameters.cpp:
(WebKit::NetworkProcessCreationParameters::encode): Encode memory pressure monitor handle.
(WebKit::NetworkProcessCreationParameters::decode): Decode memory pressure monitor handle.
* NetworkProcess/NetworkProcessCreationParameters.h:
* PlatformEfl.cmake: Add new file to compilation, and update include dirs.
* PlatformGTK.cmake: Ditto.
* PluginProcess/PluginProcess.cpp:
(WebKit::PluginProcess::initializePluginProcess): Pass the memory pressure monitor file descriptor to the MemoryPressureHandler.
* Shared/Plugins/PluginProcessCreationParameters.cpp:
(WebKit::PluginProcessCreationParameters::encode): Encode memory pressure monitor handle.
(WebKit::PluginProcessCreationParameters::decode): Decode memory pressure monitor handle.
* Shared/Plugins/PluginProcessCreationParameters.h:
* Shared/WebProcessCreationParameters.cpp:
(WebKit::WebProcessCreationParameters::encode): Encode memory pressure monitor handle.
(WebKit::WebProcessCreationParameters::decode): Decode memory pressure monitor handle.
* Shared/WebProcessCreationParameters.h:
* UIProcess/Plugins/PluginProcessProxy.cpp:
(WebKit::PluginProcessProxy::didFinishLaunching): Create the memory pressure monitor handle for the plugin
process if needed.
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::ensureNetworkProcess): Create the memory pressure monitor handle for the network
process if needed.
(WebKit::WebProcessPool::createNewWebProcess): Create the memory pressure monitor handle for the web process if
needed.
* UIProcess/linux/MemoryPressureMonitor.cpp: Added.
(WebKit::lowWatermarkPages):
(WebKit::systemPageSize):
(WebKit::calculateMemoryAvailable):
(WebKit::systemMemoryUsedAsPercentage):
(WebKit::pollIntervalForUsedMemoryPercentage):
(WebKit::isSystemdMemoryPressureMonitorAvailable):
(WebKit::MemoryPressureMonitor::isEnabled):
(WebKit::MemoryPressureMonitor::singleton):
(WebKit::MemoryPressureMonitor::MemoryPressureMonitor):
(WebKit::MemoryPressureMonitor::createHandle):
* UIProcess/linux/MemoryPressureMonitor.h:
* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::initializeWebProcess): Pass the memory pressure monitor file descriptor to the MemoryPressureHandler.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (203341 => 203342)


--- trunk/Source/WebCore/ChangeLog	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebCore/ChangeLog	2016-07-18 09:04:10 UTC (rev 203342)
@@ -1,3 +1,16 @@
+2016-07-18  Carlos Garcia Campos  <[email protected]>
+
+        MemoryPressureHandler doesn't work if cgroups aren't present in Linux
+        https://bugs.webkit.org/show_bug.cgi?id=155255
+
+        Reviewed by Sergio Villar Senin.
+
+        Allow to pass an eventFD file descriptor to the MemoryPressureHandler to be monitorized in case cgroups are not
+        available.
+
+        * platform/MemoryPressureHandler.h:
+        * platform/linux/MemoryPressureHandlerLinux.cpp:
+
 2016-07-17  Gyuyoung Kim  <[email protected]>
 
         Clean up PassRefPtr uses in Modules/encryptedmedia, Modules/speech, and Modules/quota

Modified: trunk/Source/WebCore/platform/MemoryPressureHandler.h (203341 => 203342)


--- trunk/Source/WebCore/platform/MemoryPressureHandler.h	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebCore/platform/MemoryPressureHandler.h	2016-07-18 09:04:10 UTC (rev 203342)
@@ -84,6 +84,8 @@
     WEBCORE_EXPORT void clearMemoryPressure();
     WEBCORE_EXPORT bool shouldWaitForMemoryClearMessage();
     void respondToMemoryPressureIfNeeded();
+#elif OS(LINUX)
+    void setMemoryPressureMonitorHandle(int fd);
 #endif
 
     class ReliefLogger {
@@ -179,6 +181,7 @@
     RunLoop::Timer<MemoryPressureHandler> m_holdOffTimer;
     void holdOffTimerFired();
     void logErrorAndCloseFDs(const char* error);
+    bool tryEnsureEventFD();
 #endif
 };
 

Modified: trunk/Source/WebCore/platform/linux/MemoryPressureHandlerLinux.cpp (203341 => 203342)


--- trunk/Source/WebCore/platform/linux/MemoryPressureHandlerLinux.cpp	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebCore/platform/linux/MemoryPressureHandlerLinux.cpp	2016-07-18 09:04:10 UTC (rev 203342)
@@ -193,15 +193,16 @@
     }
 }
 
-void MemoryPressureHandler::install()
+bool MemoryPressureHandler::tryEnsureEventFD()
 {
-    if (m_installed || m_holdOffTimer.isActive())
-        return;
+    if (m_eventFD)
+        return true;
 
+    // Try to use cgroups instead.
     int fd = eventfd(0, EFD_CLOEXEC);
     if (fd == -1) {
         LOG(MemoryPressure, "eventfd() failed: %m");
-        return;
+        return false;
     }
     m_eventFD = fd;
 
@@ -208,7 +209,7 @@
     fd = open(s_cgroupMemoryPressureLevel, O_CLOEXEC | O_RDONLY);
     if (fd == -1) {
         logErrorAndCloseFDs("Failed to open memory.pressure_level");
-        return;
+        return false;
     }
     m_pressureLevelFD = fd;
 
@@ -215,7 +216,7 @@
     fd = open(s_cgroupEventControl, O_CLOEXEC | O_WRONLY);
     if (fd == -1) {
         logErrorAndCloseFDs("Failed to open cgroup.event_control");
-        return;
+        return false;
     }
 
     char line[128] = {0, };
@@ -223,10 +224,21 @@
         || write(fd, line, strlen(line) + 1) < 0) {
         logErrorAndCloseFDs("Failed to write cgroup.event_control");
         close(fd);
-        return;
+        return false;
     }
     close(fd);
 
+    return true;
+}
+
+void MemoryPressureHandler::install()
+{
+    if (m_installed || m_holdOffTimer.isActive())
+        return;
+
+    if (!tryEnsureEventFD())
+        return;
+
     m_eventFDPoller = std::make_unique<EventFDPoller>(m_eventFD.value(), [this] {
         // FIXME: Current memcg does not provide any way for users to know how serious the memory pressure is.
         // So we assume all notifications from memcg are critical for now. If memcg had better inferfaces
@@ -257,7 +269,17 @@
     m_holdOffTimer.stop();
     m_eventFDPoller = nullptr;
 
-    logErrorAndCloseFDs(nullptr);
+    if (m_pressureLevelFD) {
+        close(m_pressureLevelFD.value());
+        m_pressureLevelFD = Nullopt;
+
+        // Only close the eventFD used for cgroups.
+        if (m_eventFD) {
+            close(m_eventFD.value());
+            m_eventFD = Nullopt;
+        }
+    }
+
     m_installed = false;
 }
 
@@ -309,6 +331,12 @@
     return vmSize;
 }
 
+void MemoryPressureHandler::setMemoryPressureMonitorHandle(int fd)
+{
+    ASSERT(!m_eventFD);
+    m_eventFD = fd;
+}
+
 } // namespace WebCore
 
 #endif // OS(LINUX)

Modified: trunk/Source/WebKit2/ChangeLog (203341 => 203342)


--- trunk/Source/WebKit2/ChangeLog	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/ChangeLog	2016-07-18 09:04:10 UTC (rev 203342)
@@ -1,3 +1,68 @@
+2016-07-18  Carlos Garcia Campos  <[email protected]>
+
+        MemoryPressureHandler doesn't work if cgroups aren't present in Linux
+        https://bugs.webkit.org/show_bug.cgi?id=155255
+
+        Reviewed by Sergio Villar Senin.
+
+        There's no way to get notifications about memory pressure in Linux without using cgroups that doesn't require a
+        manual polling. We can get that information from /proc/meminfo, but that's not pollable so it requires to
+        manually check its contents in a loop sleeping for a while between checks. This means we would be waking up the
+        process on every poll iteration, most of the times for nothing. That's specially problematic on devices running
+        on battery. And taking into account that there's a memory pressure handler in every secondary process (Web,
+        Network and Plugin), we would be waking up all those process all the time. However, not having a memory pressure
+        handler is even more problematic than the manual polling.
+        This patch adds a class MemoryPressureMonitor to the manual polling of /proc/meminfo, but runs in the UI
+        process, to avoid the weakups in all other secondary processes, and uses an eventFD to notify all other
+        processes. It's only used in case cgroups is not available. The eventFD descriptor is sent to all other
+        processes at startup, and passed to the MemoryPressureHandler before install() is called for the first
+        time. To minimize the wakeups even in the UI process, the poll interval is calculated from 1 to 5 seconds
+        depending on the current memory used, so in case of low memory level we sleep for a longer time.
+        It's also important to make the memory calculations as accurate as possible to avoid cleaning resources in the
+        secondary processes unnecessarily.
+
+        * NetworkProcess/NetworkProcess.cpp:
+        (WebKit::NetworkProcess::initializeNetworkProcess): Pass the memory pressure monitor file descriptor to the MemoryPressureHandler.
+        * NetworkProcess/NetworkProcess.h:
+        * NetworkProcess/NetworkProcessCreationParameters.cpp:
+        (WebKit::NetworkProcessCreationParameters::encode): Encode memory pressure monitor handle.
+        (WebKit::NetworkProcessCreationParameters::decode): Decode memory pressure monitor handle.
+        * NetworkProcess/NetworkProcessCreationParameters.h:
+        * PlatformEfl.cmake: Add new file to compilation, and update include dirs.
+        * PlatformGTK.cmake: Ditto.
+        * PluginProcess/PluginProcess.cpp:
+        (WebKit::PluginProcess::initializePluginProcess): Pass the memory pressure monitor file descriptor to the MemoryPressureHandler.
+        * Shared/Plugins/PluginProcessCreationParameters.cpp:
+        (WebKit::PluginProcessCreationParameters::encode): Encode memory pressure monitor handle.
+        (WebKit::PluginProcessCreationParameters::decode): Decode memory pressure monitor handle.
+        * Shared/Plugins/PluginProcessCreationParameters.h:
+        * Shared/WebProcessCreationParameters.cpp:
+        (WebKit::WebProcessCreationParameters::encode): Encode memory pressure monitor handle.
+        (WebKit::WebProcessCreationParameters::decode): Decode memory pressure monitor handle.
+        * Shared/WebProcessCreationParameters.h:
+        * UIProcess/Plugins/PluginProcessProxy.cpp:
+        (WebKit::PluginProcessProxy::didFinishLaunching): Create the memory pressure monitor handle for the plugin
+        process if needed.
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::ensureNetworkProcess): Create the memory pressure monitor handle for the network
+        process if needed.
+        (WebKit::WebProcessPool::createNewWebProcess): Create the memory pressure monitor handle for the web process if
+        needed.
+        * UIProcess/linux/MemoryPressureMonitor.cpp: Added.
+        (WebKit::lowWatermarkPages):
+        (WebKit::systemPageSize):
+        (WebKit::calculateMemoryAvailable):
+        (WebKit::systemMemoryUsedAsPercentage):
+        (WebKit::pollIntervalForUsedMemoryPercentage):
+        (WebKit::isSystemdMemoryPressureMonitorAvailable):
+        (WebKit::MemoryPressureMonitor::isEnabled):
+        (WebKit::MemoryPressureMonitor::singleton):
+        (WebKit::MemoryPressureMonitor::MemoryPressureMonitor):
+        (WebKit::MemoryPressureMonitor::createHandle):
+        * UIProcess/linux/MemoryPressureMonitor.h:
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::initializeWebProcess): Pass the memory pressure monitor file descriptor to the MemoryPressureHandler.
+
 2016-07-17  Carlos Garcia Campos  <[email protected]>
 
         REGRESSION(r202855): [GTK] ASSERTION FAILED: m_webPage.bounds().contains(bounds)

Modified: trunk/Source/WebKit2/NetworkProcess/NetworkProcess.cpp (203341 => 203342)


--- trunk/Source/WebKit2/NetworkProcess/NetworkProcess.cpp	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/NetworkProcess/NetworkProcess.cpp	2016-07-18 09:04:10 UTC (rev 203342)
@@ -193,7 +193,7 @@
     WTF::releaseFastMallocFreeMemory();
 }
 
-void NetworkProcess::initializeNetworkProcess(const NetworkProcessCreationParameters& parameters)
+void NetworkProcess::initializeNetworkProcess(NetworkProcessCreationParameters&& parameters)
 {
     platformInitializeNetworkProcess(parameters);
 
@@ -202,6 +202,10 @@
     m_suppressMemoryPressureHandler = parameters.shouldSuppressMemoryPressureHandler;
     if (!m_suppressMemoryPressureHandler) {
         auto& memoryPressureHandler = MemoryPressureHandler::singleton();
+#if OS(LINUX)
+        if (parameters.memoryPressureMonitorHandle.fileDescriptor() != -1)
+            memoryPressureHandler.setMemoryPressureMonitorHandle(parameters.memoryPressureMonitorHandle.releaseFileDescriptor());
+#endif
         memoryPressureHandler.setLowMemoryHandler([this] (Critical critical, Synchronous) {
             lowMemoryHandler(critical);
         });

Modified: trunk/Source/WebKit2/NetworkProcess/NetworkProcess.h (203341 => 203342)


--- trunk/Source/WebKit2/NetworkProcess/NetworkProcess.h	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/NetworkProcess/NetworkProcess.h	2016-07-18 09:04:10 UTC (rev 203342)
@@ -159,7 +159,7 @@
     // Message Handlers
     void didReceiveNetworkProcessMessage(IPC::Connection&, IPC::MessageDecoder&);
     void didReceiveSyncNetworkProcessMessage(IPC::Connection&, IPC::MessageDecoder&, std::unique_ptr<IPC::MessageEncoder>&);
-    void initializeNetworkProcess(const NetworkProcessCreationParameters&);
+    void initializeNetworkProcess(NetworkProcessCreationParameters&&);
     void createNetworkConnectionToWebProcess();
     void destroyPrivateBrowsingSession(WebCore::SessionID);
 

Modified: trunk/Source/WebKit2/NetworkProcess/NetworkProcessCreationParameters.cpp (203341 => 203342)


--- trunk/Source/WebKit2/NetworkProcess/NetworkProcessCreationParameters.cpp	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/NetworkProcess/NetworkProcessCreationParameters.cpp	2016-07-18 09:04:10 UTC (rev 203342)
@@ -82,6 +82,9 @@
     encoder << ignoreTLSErrors;
     encoder << languages;
 #endif
+#if OS(LINUX)
+    encoder << memoryPressureMonitorHandle;
+#endif
 }
 
 bool NetworkProcessCreationParameters::decode(IPC::ArgumentDecoder& decoder, NetworkProcessCreationParameters& result)
@@ -158,6 +161,11 @@
         return false;
 #endif
 
+#if OS(LINUX)
+    if (!decoder.decode(result.memoryPressureMonitorHandle))
+        return false;
+#endif
+
     return true;
 }
 

Modified: trunk/Source/WebKit2/NetworkProcess/NetworkProcessCreationParameters.h (203341 => 203342)


--- trunk/Source/WebKit2/NetworkProcess/NetworkProcessCreationParameters.h	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/NetworkProcess/NetworkProcessCreationParameters.h	2016-07-18 09:04:10 UTC (rev 203342)
@@ -26,6 +26,7 @@
 #ifndef NetworkProcessCreationParameters_h
 #define NetworkProcessCreationParameters_h
 
+#include "Attachment.h"
 #include "CacheModel.h"
 #include "SandboxExtension.h"
 #include <wtf/Vector.h>
@@ -95,6 +96,10 @@
     bool ignoreTLSErrors;
     Vector<String> languages;
 #endif
+
+#if OS(LINUX)
+    IPC::Attachment memoryPressureMonitorHandle;
+#endif
 };
 
 } // namespace WebKit

Modified: trunk/Source/WebKit2/PlatformEfl.cmake (203341 => 203342)


--- trunk/Source/WebKit2/PlatformEfl.cmake	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/PlatformEfl.cmake	2016-07-18 09:04:10 UTC (rev 203342)
@@ -171,6 +171,8 @@
     UIProcess/gstreamer/InstallMissingMediaPluginsPermissionRequest.cpp
     UIProcess/gstreamer/WebPageProxyGStreamer.cpp
 
+    UIProcess/linux/MemoryPressureMonitor.cpp
+
     UIProcess/soup/WebCookieManagerProxySoup.cpp
     UIProcess/soup/WebProcessPoolSoup.cpp
 
@@ -256,6 +258,7 @@
     "${WEBKIT2_DIR}/UIProcess/CoordinatedGraphics"
     "${WEBKIT2_DIR}/UIProcess/Network/CustomProtocols/soup"
     "${WEBKIT2_DIR}/UIProcess/efl"
+    "${WEBKIT2_DIR}/UIProcess/linux"
     "${WEBKIT2_DIR}/UIProcess/soup"
     "${WEBKIT2_DIR}/WebProcess/efl"
     "${WEBKIT2_DIR}/WebProcess/soup"

Modified: trunk/Source/WebKit2/PlatformGTK.cmake (203341 => 203342)


--- trunk/Source/WebKit2/PlatformGTK.cmake	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/PlatformGTK.cmake	2016-07-18 09:04:10 UTC (rev 203342)
@@ -273,6 +273,8 @@
 
     UIProcess/Launcher/gtk/ProcessLauncherGtk.cpp
 
+    UIProcess/linux/MemoryPressureMonitor.cpp
+
     UIProcess/Network/CustomProtocols/soup/CustomProtocolManagerProxySoup.cpp
     UIProcess/Network/CustomProtocols/soup/WebSoupCustomProtocolRequestManager.cpp
     UIProcess/Network/CustomProtocols/soup/WebSoupCustomProtocolRequestManagerClient.cpp
@@ -513,6 +515,7 @@
     "${WEBKIT2_DIR}/UIProcess/Plugins/gtk"
     "${WEBKIT2_DIR}/UIProcess/gstreamer"
     "${WEBKIT2_DIR}/UIProcess/gtk"
+    "${WEBKIT2_DIR}/UIProcess/linux"
     "${WEBKIT2_DIR}/UIProcess/soup"
     "${WEBKIT2_DIR}/WebProcess/InjectedBundle/API/gtk"
     "${WEBKIT2_DIR}/WebProcess/Plugins/Netscape/unix"

Modified: trunk/Source/WebKit2/PluginProcess/PluginProcess.cpp (203341 => 203342)


--- trunk/Source/WebKit2/PluginProcess/PluginProcess.cpp	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/PluginProcess/PluginProcess.cpp	2016-07-18 09:04:10 UTC (rev 203342)
@@ -71,13 +71,6 @@
 {
     m_pluginPath = parameters.extraInitializationData.get("plugin-path");
     platformInitializeProcess(parameters);
-
-    auto& memoryPressureHandler = MemoryPressureHandler::singleton();
-    memoryPressureHandler.setLowMemoryHandler([this] (Critical, Synchronous) {
-        if (shouldTerminate())
-            terminate();
-    });
-    memoryPressureHandler.install();
 }
 
 void PluginProcess::removeWebProcessConnection(WebProcessConnection* webProcessConnection)
@@ -137,6 +130,17 @@
 {
     ASSERT(!m_pluginModule);
 
+    auto& memoryPressureHandler = MemoryPressureHandler::singleton();
+#if OS(LINUX)
+    if (parameters.memoryPressureMonitorHandle.fileDescriptor() != -1)
+        memoryPressureHandler.setMemoryPressureMonitorHandle(parameters.memoryPressureMonitorHandle.releaseFileDescriptor());
+#endif
+    memoryPressureHandler.setLowMemoryHandler([this] (Critical, Synchronous) {
+        if (shouldTerminate())
+            terminate();
+    });
+    memoryPressureHandler.install();
+
     m_supportsAsynchronousPluginInitialization = parameters.supportsAsynchronousPluginInitialization;
     setMinimumLifetime(parameters.minimumLifetime);
     setTerminationTimeout(parameters.terminationTimeout);

Modified: trunk/Source/WebKit2/Shared/Plugins/PluginProcessCreationParameters.cpp (203341 => 203342)


--- trunk/Source/WebKit2/Shared/Plugins/PluginProcessCreationParameters.cpp	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/Shared/Plugins/PluginProcessCreationParameters.cpp	2016-07-18 09:04:10 UTC (rev 203342)
@@ -51,6 +51,9 @@
     IPC::encode(encoder, networkATSContext.get());
 #endif
 #endif
+#if OS(LINUX)
+    encoder << memoryPressureMonitorHandle;
+#endif
 }
 
 bool PluginProcessCreationParameters::decode(IPC::ArgumentDecoder& decoder, PluginProcessCreationParameters& result)
@@ -71,6 +74,10 @@
         return false;
 #endif
 #endif
+#if OS(LINUX)
+    if (!decoder.decode(result.memoryPressureMonitorHandle))
+        return false;
+#endif
 
     return true;
 }

Modified: trunk/Source/WebKit2/Shared/Plugins/PluginProcessCreationParameters.h (203341 => 203342)


--- trunk/Source/WebKit2/Shared/Plugins/PluginProcessCreationParameters.h	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/Shared/Plugins/PluginProcessCreationParameters.h	2016-07-18 09:04:10 UTC (rev 203342)
@@ -28,6 +28,7 @@
 
 #if ENABLE(NETSCAPE_PLUGIN_API)
 
+#include "Attachment.h"
 #include "PluginProcessAttributes.h"
 
 #if PLATFORM(COCOA)
@@ -59,6 +60,9 @@
     RetainPtr<CFDataRef> networkATSContext;
 #endif
 #endif
+#if OS(LINUX)
+    IPC::Attachment memoryPressureMonitorHandle;
+#endif
 };
 
 } // namespace WebKit

Modified: trunk/Source/WebKit2/Shared/WebProcessCreationParameters.cpp (203341 => 203342)


--- trunk/Source/WebKit2/Shared/WebProcessCreationParameters.cpp	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/Shared/WebProcessCreationParameters.cpp	2016-07-18 09:04:10 UTC (rev 203342)
@@ -141,6 +141,10 @@
 #if TARGET_OS_IPHONE || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
     IPC::encode(encoder, networkATSContext.get());
 #endif
+
+#if OS(LINUX)
+    encoder << memoryPressureMonitorHandle;
+#endif
 }
 
 bool WebProcessCreationParameters::decode(IPC::ArgumentDecoder& decoder, WebProcessCreationParameters& parameters)
@@ -296,6 +300,11 @@
         return false;
 #endif
 
+#if OS(LINUX)
+    if (!decoder.decode(parameters.memoryPressureMonitorHandle))
+        return false;
+#endif
+
     return true;
 }
 

Modified: trunk/Source/WebKit2/Shared/WebProcessCreationParameters.h (203341 => 203342)


--- trunk/Source/WebKit2/Shared/WebProcessCreationParameters.h	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/Shared/WebProcessCreationParameters.h	2016-07-18 09:04:10 UTC (rev 203342)
@@ -166,6 +166,10 @@
 #if TARGET_OS_IPHONE || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
     RetainPtr<CFDataRef> networkATSContext;
 #endif
+
+#if OS(LINUX)
+    IPC::Attachment memoryPressureMonitorHandle;
+#endif
 };
 
 } // namespace WebKit

Modified: trunk/Source/WebKit2/UIProcess/Plugins/PluginProcessProxy.cpp (203341 => 203342)


--- trunk/Source/WebKit2/UIProcess/Plugins/PluginProcessProxy.cpp	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/UIProcess/Plugins/PluginProcessProxy.cpp	2016-07-18 09:04:10 UTC (rev 203342)
@@ -38,6 +38,10 @@
 #include <WebCore/NotImplemented.h>
 #include <wtf/RunLoop.h>
 
+#if OS(LINUX)
+#include "MemoryPressureMonitor.h"
+#endif
+
 using namespace WebCore;
 
 namespace WebKit {
@@ -229,6 +233,12 @@
         parameters.minimumLifetime = minimumLifetime;
         parameters.terminationTimeout = shutdownTimeout;
     }
+
+#if OS(LINUX)
+    if (MemoryPressureMonitor::isEnabled())
+        parameters.memoryPressureMonitorHandle = MemoryPressureMonitor::singleton().createHandle();
+#endif
+
     platformInitializePluginProcess(parameters);
 
     // Initialize the plug-in host process.

Modified: trunk/Source/WebKit2/UIProcess/WebProcessPool.cpp (203341 => 203342)


--- trunk/Source/WebKit2/UIProcess/WebProcessPool.cpp	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/UIProcess/WebProcessPool.cpp	2016-07-18 09:04:10 UTC (rev 203342)
@@ -93,6 +93,10 @@
 #include "WebSoupCustomProtocolRequestManager.h"
 #endif
 
+#if OS(LINUX)
+#include "MemoryPressureMonitor.h"
+#endif
+
 #ifndef NDEBUG
 #include <wtf/RefCountedLeakCounter.h>
 #endif
@@ -370,6 +374,11 @@
         SandboxExtension::createHandle(parentBundleDirectory, SandboxExtension::ReadOnly, parameters.parentBundleDirectoryExtensionHandle);
 #endif
 
+#if OS(LINUX)
+    if (MemoryPressureMonitor::isEnabled())
+        parameters.memoryPressureMonitorHandle = MemoryPressureMonitor::singleton().createHandle();
+#endif
+
     parameters.shouldUseTestingNetworkSession = m_shouldUseTestingNetworkSession;
 
     // Add any platform specific parameters
@@ -626,6 +635,8 @@
 
 #if OS(LINUX)
     parameters.shouldEnableMemoryPressureReliefLogging = true;
+    if (MemoryPressureMonitor::isEnabled())
+        parameters.memoryPressureMonitorHandle = MemoryPressureMonitor::singleton().createHandle();
 #endif
 
     parameters.resourceLoadStatisticsEnabled = resourceLoadStatisticsEnabled();

Added: trunk/Source/WebKit2/UIProcess/linux/MemoryPressureMonitor.cpp (0 => 203342)


--- trunk/Source/WebKit2/UIProcess/linux/MemoryPressureMonitor.cpp	                        (rev 0)
+++ trunk/Source/WebKit2/UIProcess/linux/MemoryPressureMonitor.cpp	2016-07-18 09:04:10 UTC (rev 203342)
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2016 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "MemoryPressureMonitor.h"
+
+#if OS(LINUX)
+
+#include "Attachment.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <mutex>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/eventfd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/Threading.h>
+#include <wtf/UniStdExtras.h>
+
+namespace WebKit {
+
+static const size_t notSet = static_cast<size_t>(-1);
+
+static const double s_minPollingIntervalInSeconds = 1;
+static const double s_maxPollingIntervalInSeconds = 5;
+static const double s_minUsedMemoryPercentageForPolling = 50;
+static const double s_maxUsedMemoryPercentageForPolling = 90;
+static const int s_memoryPresurePercentageThreshold = 95;
+
+static size_t lowWatermarkPages()
+{
+    FILE* file = fopen("/proc/zoneinfo", "r");
+    if (!file)
+        return notSet;
+
+    size_t low = 0;
+    bool inZone = false;
+    bool foundLow = false;
+    char buffer[128];
+    while (char* line = fgets(buffer, 128, file)) {
+        if (!strcmp(line, "Node")) {
+            inZone = true;
+            foundLow = false;
+            continue;
+        }
+
+        char* token = strtok(line, " ");
+        if (!token)
+            continue;
+
+        if (!strcmp(token, "low")) {
+            if (!inZone || foundLow) {
+                low = notSet;
+                break;
+            }
+            token = strtok(nullptr, " ");
+            if (!token) {
+                low = notSet;
+                break;
+            }
+            low += atoll(token);
+            foundLow = true;
+        }
+    }
+    fclose(file);
+
+    return low;
+}
+
+static inline size_t systemPageSize()
+{
+    static size_t pageSize = 0;
+    if (!pageSize)
+        pageSize = sysconf(_SC_PAGE_SIZE);
+    return pageSize;
+}
+
+// If MemAvailable was not present in /proc/meminfo, because it's an old kernel version,
+// we can do the same calculation with the information we have from meminfo and the low watermaks.
+// See https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
+static size_t calculateMemoryAvailable(size_t memoryFree, size_t activeFile, size_t inactiveFile, size_t slabReclaimable)
+{
+    if (memoryFree == notSet || activeFile == notSet || inactiveFile == notSet || slabReclaimable == notSet)
+        return notSet;
+
+    size_t lowWatermark = lowWatermarkPages();
+    if (lowWatermark == notSet)
+        return notSet;
+
+    lowWatermark *= systemPageSize() / KB;
+
+    // Estimate the amount of memory available for userspace allocations, without causing swapping.
+    // Free memory cannot be taken below the low watermark, before the system starts swapping.
+    lowWatermark *= systemPageSize() / KB;
+    size_t memoryAvailable = memoryFree - lowWatermark;
+
+    // Not all the page cache can be freed, otherwise the system will start swapping. Assume at least
+    // half of the page cache, or the low watermark worth of cache, needs to stay.
+    size_t pageCache = activeFile + inactiveFile;
+    pageCache -= std::min(pageCache / 2, lowWatermark);
+    memoryAvailable += pageCache;
+
+    // Part of the reclaimable slab consists of items that are in use, and cannot be freed.
+    // Cap this estimate at the low watermark.
+    memoryAvailable += slabReclaimable - std::min(slabReclaimable / 2, lowWatermark);
+    return memoryAvailable;
+}
+
+static int systemMemoryUsedAsPercentage()
+{
+    FILE* file = fopen("/proc/meminfo", "r");
+    if (!file)
+        return -1;
+
+    size_t memoryAvailable, memoryTotal, memoryFree, activeFile, inactiveFile, slabReclaimable;
+    memoryAvailable = memoryTotal = memoryFree = activeFile = inactiveFile = slabReclaimable = notSet;
+    char buffer[128];
+    while (char* line = fgets(buffer, 128, file)) {
+        char* token = strtok(line, " ");
+        if (!token)
+            break;
+
+        if (!strcmp(token, "MemAvailable:")) {
+            if ((token = strtok(nullptr, " "))) {
+                memoryAvailable = atoll(token);
+                if (memoryTotal != notSet)
+                    break;
+            }
+        } else if (!strcmp(token, "MemTotal:")) {
+            if ((token = strtok(nullptr, " ")))
+                memoryTotal = atoll(token);
+            else
+                break;
+        } else if (!strcmp(token, "MemFree:")) {
+            if ((token = strtok(nullptr, " ")))
+                memoryFree = atoll(token);
+            else
+                break;
+        } else if (!strcmp(token, "Active(file):")) {
+            if ((token = strtok(nullptr, " ")))
+                activeFile = atoll(token);
+            else
+                break;
+        } else if (!strcmp(token, "Inactive(file):")) {
+            if ((token = strtok(nullptr, " ")))
+                inactiveFile = atoll(token);
+            else
+                break;
+        } else if (!strcmp(token, "SReclaimable:")) {
+            if ((token = strtok(nullptr, " ")))
+                slabReclaimable = atoll(token);
+            else
+                break;
+        }
+
+        if (memoryTotal != notSet && memoryFree != notSet && activeFile != notSet && inactiveFile != notSet && slabReclaimable != notSet)
+            break;
+    }
+    fclose(file);
+
+    if (!memoryTotal || memoryTotal == notSet)
+        return -1;
+
+    if (memoryAvailable == notSet) {
+        memoryAvailable = calculateMemoryAvailable(memoryFree, activeFile, inactiveFile, slabReclaimable);
+        if (memoryAvailable == notSet)
+            return -1;
+    }
+
+    if (memoryAvailable > memoryTotal)
+        return -1;
+
+    return ((memoryTotal - memoryAvailable) * 100) / memoryTotal;
+}
+
+static inline double pollIntervalForUsedMemoryPercentage(int usedPercentage)
+{
+    // Use a different poll interval depending on the currently memory used,
+    // to avoid polling too often when the system is under low memory usage.
+    if (usedPercentage < s_minUsedMemoryPercentageForPolling)
+        return s_maxPollingIntervalInSeconds;
+
+    if (usedPercentage >= s_maxUsedMemoryPercentageForPolling)
+        return s_minPollingIntervalInSeconds;
+
+    return s_minPollingIntervalInSeconds + (s_maxPollingIntervalInSeconds - s_minPollingIntervalInSeconds) *
+        ((usedPercentage - s_minUsedMemoryPercentageForPolling) / (s_maxUsedMemoryPercentageForPolling - s_minUsedMemoryPercentageForPolling));
+}
+
+static bool isSystemdMemoryPressureMonitorAvailable()
+{
+    int fd = open("/sys/fs/cgroup/memory/memory.pressure_level", O_CLOEXEC | O_RDONLY);
+    if (fd == -1)
+        return false;
+    close(fd);
+
+    fd = open("/sys/fs/cgroup/memory/cgroup.event_control", O_CLOEXEC | O_WRONLY);
+    if (fd == -1)
+        return false;
+    close(fd);
+
+    return true;
+}
+
+bool MemoryPressureMonitor::isEnabled()
+{
+    static std::once_flag onceFlag;
+    static bool enabled;
+    std::call_once(onceFlag, [] { enabled = !isSystemdMemoryPressureMonitorAvailable(); });
+    return enabled;
+}
+
+MemoryPressureMonitor& MemoryPressureMonitor::singleton()
+{
+    ASSERT(isEnabled());
+    static NeverDestroyed<MemoryPressureMonitor> memoryMonitor;
+    return memoryMonitor;
+}
+
+MemoryPressureMonitor::MemoryPressureMonitor()
+    : m_eventFD(eventfd(0, EFD_CLOEXEC))
+{
+    if (m_eventFD == -1)
+        return;
+
+    ThreadIdentifier threadIdentifier = createThread("MemoryPressureMonitor", [this] {
+        double pollInterval = s_maxPollingIntervalInSeconds;
+        while (true) {
+            sleep(pollInterval);
+
+            int usedPercentage = systemMemoryUsedAsPercentage();
+            if (usedPercentage >= s_memoryPresurePercentageThreshold) {
+                uint64_t fdEvent = 1;
+                ssize_t bytesWritten = write(m_eventFD, &fdEvent, sizeof(uint64_t));
+                if (bytesWritten != sizeof(uint64_t)) {
+                    WTFLogAlways("Error writing to MemoryPressureMonitor eventFD: %s", strerror(errno));
+                    break;
+                }
+            }
+            pollInterval = pollIntervalForUsedMemoryPercentage(usedPercentage);
+        }
+        close(m_eventFD);
+    });
+    detachThread(threadIdentifier);
+}
+
+IPC::Attachment MemoryPressureMonitor::createHandle() const
+{
+    int duplicatedHandle = dupCloseOnExec(m_eventFD);
+    if (duplicatedHandle == -1)
+        return { };
+    return IPC::Attachment(duplicatedHandle);
+}
+
+} // namespace WebKit
+
+#endif // OS(LINUX)

Copied: trunk/Source/WebKit2/UIProcess/linux/MemoryPressureMonitor.h (from rev 203341, trunk/Source/WebKit2/Shared/Plugins/PluginProcessCreationParameters.h) (0 => 203342)


--- trunk/Source/WebKit2/UIProcess/linux/MemoryPressureMonitor.h	                        (rev 0)
+++ trunk/Source/WebKit2/UIProcess/linux/MemoryPressureMonitor.h	2016-07-18 09:04:10 UTC (rev 203342)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if OS(LINUX)
+
+#include <wtf/NeverDestroyed.h>
+#include <wtf/Noncopyable.h>
+
+namespace IPC {
+class Attachment;
+}
+
+namespace WebKit {
+
+class MemoryPressureMonitor {
+    WTF_MAKE_NONCOPYABLE(MemoryPressureMonitor);
+    friend class NeverDestroyed<MemoryPressureMonitor>;
+public:
+    static MemoryPressureMonitor& singleton();
+    static bool isEnabled();
+
+    ~MemoryPressureMonitor();
+
+    IPC::Attachment createHandle() const;
+
+private:
+    MemoryPressureMonitor();
+
+    int m_eventFD { -1 };
+};
+
+} // namespace WebKit
+
+#endif // OS(LINUX)

Modified: trunk/Source/WebKit2/WebProcess/WebProcess.cpp (203341 => 203342)


--- trunk/Source/WebKit2/WebProcess/WebProcess.cpp	2016-07-18 06:53:52 UTC (rev 203341)
+++ trunk/Source/WebKit2/WebProcess/WebProcess.cpp	2016-07-18 09:04:10 UTC (rev 203342)
@@ -267,7 +267,9 @@
     ASSERT(m_pageMap.isEmpty());
 
 #if OS(LINUX)
-    WebCore::MemoryPressureHandler::ReliefLogger::setLoggingEnabled(parameters.shouldEnableMemoryPressureReliefLogging);
+    if (parameters.memoryPressureMonitorHandle.fileDescriptor() != -1)
+        MemoryPressureHandler::singleton().setMemoryPressureMonitorHandle(parameters.memoryPressureMonitorHandle.releaseFileDescriptor());
+    MemoryPressureHandler::ReliefLogger::setLoggingEnabled(parameters.shouldEnableMemoryPressureReliefLogging);
 #endif
 
     platformInitializeWebProcess(WTFMove(parameters));
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to