linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/2] Memory notification pseudo-device module
@ 2012-01-17 13:22 Leonid Moiseichuk
  2012-01-17 13:22 ` [PATCH v2 1/2] Making si_swapinfo exportable Leonid Moiseichuk
  2012-01-17 13:22 ` [PATCH v2 2/2] Memory notification pseudo-device module Leonid Moiseichuk
  0 siblings, 2 replies; 14+ messages in thread
From: Leonid Moiseichuk @ 2012-01-17 13:22 UTC (permalink / raw)
  To: linux-mm, linux-kernel
  Cc: cesarb, kamezawa.hiroyu, emunson, penberg, aarcange, riel, mel,
	rientjes, dima, gregkh, rebecca, san, akpm, vesa.jaaskelainen

Hello,

That is a continuation of Used Memory Meter (UMM) started as [1] and re-designed
according to inputs I can implement. 

The main idea of memnotify is to provide low-cost interface for user-space to
update with specified granularity and timeout required memory usulization values
in specified moment of time. It is not a low memory interface as [2] nor OOM killer
but it could be used for situations related to "close-to-OOM" handling. The examples
of usage could be discovered in libmemnotify [3] or test case code below this intro.

During the previous discussion two biggest disappointments were discovered:
1. hooking MM -- now it is removed, the new solution has 0 MM changes, only extra
   shrinker added to re-enforce timer to re-check memory situation as soon as possible
2. extendability -- to add any other tracked value you need to modify 2 places in 
   code (memtypes[] and get_memory_status() function).

As Kosaki-san said for activity tracking I added "active" page set. You can subscribe
for one or several values for tracking simultaneously.

To periodic re-read vm_stat (using global_page_state() interface) I use deferred timer,
expecting if cpu sleeps timer could be delayed and use-time will be not affected. 
If this assumption is not correct I can add register_cpu_notifier() call. Otherwise,
when no clients connected - no activity or subscription in module performed.

The number of module parameters could be specified to adjust reaction time or
granularity of changes. The interface pseudo-device hardcoded as /dev/memnotify.

As in previous time module tested using arm, x86-32 and x86-64 for typical (10 clients)
and stress (10K clients) cases.

With Best Regards,
Leonid

References:
1. https://lkml.org/lkml/2012/1/4/208
2. https://lkml.org/lkml/2012/1/17/34
3. http://maemo.gitorious.org/maemo-tools/libmemnotify

Leonid Moiseichuk (2):
  Making si_swapinfo exportable
  Memory notification pseudo-device module

 drivers/misc/Kconfig     |   11 +
 drivers/misc/Makefile    |    1 +
 drivers/misc/memnotify.c |  582 ++++++++++++++++++++++++++++++++++++++++++++++
 mm/swapfile.c            |    3 +
 4 files changed, 597 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/memnotify.c

-- 
1.7.7.3

/*
 * mn_test.c - test for system-wide memory notification/meter implementation
 * 
 * Usage:
 *    $gcc -o mn_test mn_test.c
 *    $mn_test
 *  or with pointing pages as threshold(s)
 *    $mn_test "used 5000"
 *  or
 *    $mn_test "active 8000 used 16000"
 *
 * Copyright (C) 2012 Nokia Corporation.
 *      Leonid Moiseichuk
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
 * kind, whether express or implied; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(const int argc, const char* argv[])
{
	const char *dev = "/dev/memnotify";
	const int fd = open(dev,O_RDWR);
	const char *lim = (2 == argc && argv[1] ? argv[1] : NULL);
	ssize_t ret;
	struct pollfd fds[1];
	char tmp[256];

	printf ("%s -- test for system-wide memory notification/meter %s\n", argv[0], dev);
	if (lim && 0 == strcmp(lim, "--help")) {
		printf ("usage: %s [threshold(s)_in_pages]\n", argv[0]);
		printf ("example of threshold(s) available in %s\n", dev);
		return -1;
	}

	if (fd < 0) {
		printf ("cannot open device %s, do you have memnotify.ko loaded?\n", dev);
		return -1;
	} else {
		printf ("device %s opened successfuly with fd = %d\n", dev, fd);
	}

	if ((ret = read(fd, tmp, sizeof(tmp))) < 0) {
		printf ("reading from %s failed\n", dev);
		return -1;
	}
	tmp[ret - 1] = 0;
	printf ("read from %s %d bytes: '%s'\n", dev, ret, tmp);

	/* do we have a threshold? */
	if ( !lim )
		return 0;

	printf ("establishing threshold '%s' and waiting\n", lim);
	if ((ret = write(fd, argv[1], strlen(argv[1]))) < 0) {
		printf ("cannot set threshold '%s' for device %s\n", lim, dev);
		return -1;
	}
	
	printf ("polling for threshold '%s' to be changed to up/down\n", lim);
	fds->fd      = fd;
	fds->events  = POLLIN;
	fds->revents = 0;
	ret = poll(fds, 1, -1);
	printf ("poll(%d) returned %d\n", fd, ret);

	if ((ret = read(fd, tmp, sizeof(tmp))) < 0) {
		printf ("reading from %s failed\n", dev);
		return -1;
	}
	tmp[ret - 1] = 0;
	printf ("--> memory figures reached '%s'\n", tmp);

	return 0;
}
/* ---< end of mn_test.c >---- */

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v2 1/2] Making si_swapinfo exportable
  2012-01-17 13:22 [PATCH v2 0/2] Memory notification pseudo-device module Leonid Moiseichuk
@ 2012-01-17 13:22 ` Leonid Moiseichuk
  2012-01-18 10:34   ` Pekka Enberg
  2012-01-17 13:22 ` [PATCH v2 2/2] Memory notification pseudo-device module Leonid Moiseichuk
  1 sibling, 1 reply; 14+ messages in thread
From: Leonid Moiseichuk @ 2012-01-17 13:22 UTC (permalink / raw)
  To: linux-mm, linux-kernel
  Cc: cesarb, kamezawa.hiroyu, emunson, penberg, aarcange, riel, mel,
	rientjes, dima, gregkh, rebecca, san, akpm, vesa.jaaskelainen

If we will make si_swapinfo() exportable it could be called from modules.
Otherwise modules have no interface to obtain information about swap usage.
Change made in the same way as si_meminfo() declared.

Signed-off-by: Leonid Moiseichuk <leonid.moiseichuk@nokia.com>
---
 mm/swapfile.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/mm/swapfile.c b/mm/swapfile.c
index b1cd120..192cc25 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -5,10 +5,12 @@
  *  Swap reorganised 29.12.95, Stephen Tweedie
  */
 
+#include <linux/export.h>
 #include <linux/mm.h>
 #include <linux/hugetlb.h>
 #include <linux/mman.h>
 #include <linux/slab.h>
+#include <linux/kernel.h>
 #include <linux/kernel_stat.h>
 #include <linux/swap.h>
 #include <linux/vmalloc.h>
@@ -2177,6 +2179,7 @@ void si_swapinfo(struct sysinfo *val)
 	val->totalswap = total_swap_pages + nr_to_be_unused;
 	spin_unlock(&swap_lock);
 }
+EXPORT_SYMBOL(si_swapinfo);
 
 /*
  * Verify that a swap entry is valid and increment its swap map count.
-- 
1.7.7.3


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH v2 2/2] Memory notification pseudo-device module
  2012-01-17 13:22 [PATCH v2 0/2] Memory notification pseudo-device module Leonid Moiseichuk
  2012-01-17 13:22 ` [PATCH v2 1/2] Making si_swapinfo exportable Leonid Moiseichuk
@ 2012-01-17 13:22 ` Leonid Moiseichuk
  2012-01-17 13:32   ` Pekka Enberg
  1 sibling, 1 reply; 14+ messages in thread
From: Leonid Moiseichuk @ 2012-01-17 13:22 UTC (permalink / raw)
  To: linux-mm, linux-kernel
  Cc: cesarb, kamezawa.hiroyu, emunson, penberg, aarcange, riel, mel,
	rientjes, dima, gregkh, rebecca, san, akpm, vesa.jaaskelainen

The memory notification (memnotify) device tracks level of memory utilization,
active page set and notifies subscribed processes when consumption crossed
specified threshold(s) up or down. It could be used on embedded devices to
implementation of performance-cheap memory reacting by using
e.g. libmemnotify or similar user-space component.

The minimal (250 ms) and maximal (15s) periods of reaction and granularity
(~1.4% of memory size) could be tuned using module options.

Signed-off-by: Leonid Moiseichuk <leonid.moiseichuk@nokia.com>
---
 drivers/misc/Kconfig     |   11 +
 drivers/misc/Makefile    |    1 +
 drivers/misc/memnotify.c |  582 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 594 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/memnotify.c

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 5664696..eefda14 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -500,6 +500,17 @@ config USB_SWITCH_FSA9480
 	  stereo and mono audio, video, microphone and UART data to use
 	  a common connector port.
 
+config MEMNOTIFY
+	tristate "Enables memory notification pseudo-device"
+	default n
+	help
+	  This option enables pseudo-device /dev/memnotify for tracking
+	  system memory utilization and updating state to subscribed clients
+	  when specified threshold reached.
+
+	  Say Y here if you want to support memory monitoring.
+	  If unsure, say N.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index b26495a..86f6199 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -48,3 +48,4 @@ obj-y				+= lis3lv02d/
 obj-y				+= carma/
 obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
 obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
+obj-$(CONFIG_MEMNOTIFY)		+= memnotify.o
diff --git a/drivers/misc/memnotify.c b/drivers/misc/memnotify.c
new file mode 100644
index 0000000..898f3df
--- /dev/null
+++ b/drivers/misc/memnotify.c
@@ -0,0 +1,582 @@
+/*
+ * memnotify.c - system-wide memory meter and notifier pseudo-device
+ *
+ * Copyright (C) 2012 Nokia Corporation.
+ *      Leonid Moiseichuk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* #define DEBUG */
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/atomic.h>
+#include <linux/jiffies.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/mmzone.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/highmem.h>
+#include <linux/swap.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/spinlock_types.h>
+#include <linux/timer.h>
+
+
+/*
+ * How often [ms] information will be updated.
+ */
+#define MN_UPDATE_PERIOD	250
+
+/*
+ * Maximal delay [ms] if no changes detected.
+ */
+#define MN_MAX_UPDATE_PERIOD	(15 * 1000)
+
+/*
+ * Which minimal [kb] allocation change will produce notification for user-space
+ * to avoid too often jittering.
+ */
+#define MN_UPDATE_SPACE	1024
+
+/*
+ * Which memory types we should have, report and track
+ *
+ * Note:
+ * If you need to report more values, add them here and
+ * modify get_memory_status function to fill more fields
+ *
+ * Warning:
+ * The length of list is limited by used flags mask (unsigned = 32)
+ */
+static const char * const memtypes[] = {
+	"total",
+	"used",
+	"active"
+};
+#define MN_TYPES_SIZE		(ARRAY_SIZE(memtypes))
+#define MN_LINE_BUFFER_SIZE	(MN_TYPES_SIZE * 64)
+
+/* Memory values indexed by memtypes */
+struct memvalue {
+	unsigned long	v[MN_TYPES_SIZE];
+};
+
+
+/* subscriber information to be notified when level changed */
+struct observer {
+	/* list data to check from notify_memory_usage and wakeup user-space */
+	struct list_head	list;
+
+	/* related file structure for open/close/read/write and poll */
+	struct file		*file;
+	/* thresholds [pages] when we should trigger notification */
+	struct memvalue		threshold;
+	/* bitmask: did we crossed theshold on last validation? */
+	unsigned		active;
+	/* flag about new notification is required */
+	bool			updated;
+};
+
+
+
+MODULE_AUTHOR("Leonid Moiseichuk (leonid.moiseichuk@nokia.com)");
+MODULE_DESCRIPTION("System memory meter/notification pseudo-device");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.2.0");
+
+static unsigned update_period __read_mostly = MN_UPDATE_PERIOD;
+module_param(update_period, uint, 0);
+MODULE_PARM_DESC(update_period, "Base update interval [ms]");
+
+static unsigned max_update_period __read_mostly = MN_MAX_UPDATE_PERIOD;
+module_param(max_update_period, uint, 0);
+MODULE_PARM_DESC(max_update_period, "Maximal update interval [ms]");
+
+static unsigned update_space __read_mostly;
+module_param(update_space, uint, 0);
+MODULE_PARM_DESC(update_space, "Clients granularity space in [kb], 0 - auto");
+
+/* The device pointer, mostly for dev_XXX */
+static struct device *dev __read_mostly;
+
+/* Validated parameters in adequate units */
+static unsigned long update_period_jiffies     __read_mostly;
+static unsigned long max_update_period_jiffies __read_mostly;
+static unsigned      update_space_pages        __read_mostly;
+
+/* Memory values which is used in measurements and notification */
+static unsigned long available_pages      __read_mostly;
+#ifdef CONFIG_SWAP
+static unsigned long available_swap_pages __read_mostly;
+#endif
+
+/* Amount of memory measured and notified last time */
+/* That is safe to have these values as a normal data by design */
+static struct memvalue	last_measured	__read_mostly;
+static struct memvalue	last_notified	__read_mostly;
+
+/* Timer which is used to requesting vm statistics */
+static struct timer_list timer;
+static unsigned long update_timer_jiffies __read_mostly;
+
+/* Subscribers in poll() call to be validated and notified */
+static atomic_t observer_counter = ATOMIC_INIT(0);
+static DEFINE_SPINLOCK(observer_lock);
+static LIST_HEAD(observer_list);
+static DECLARE_WAIT_QUEUE_HEAD(watcher_queue);
+
+
+
+/* Validates two memvalues to be equal or not */
+static inline bool memvalue_about_equal(
+	const struct memvalue *a, const struct memvalue *b)
+{
+	unsigned m = 1;
+	unsigned i = 0;
+
+	while (i < MN_TYPES_SIZE) {
+		const unsigned long av = a->v[i];
+		const unsigned long bv = b->v[i];
+		/* if field set to zero = not tracked */
+		if (av && bv &&
+			(av < bv ? bv - av : av - bv) >= update_space_pages)
+			return false;
+		m <<= 1;
+		i++;
+	}
+
+	return true;
+}
+
+/* Produces bitmask of active memory thresholds */
+static inline unsigned memvalue_active(
+	const struct memvalue *t, const struct memvalue *c)
+{
+	unsigned a = 0;
+	unsigned m = 1;
+	unsigned i = 0;
+
+	while (i < MN_TYPES_SIZE) {
+		const unsigned long tv = t->v[i];
+		const unsigned long cv = c->v[i];
+		/* if field set to zero = not tracked */
+		if (tv && cv && cv >= tv)
+			a |= m;
+		m <<= 1;
+		i++;
+	}
+
+	return a;
+}
+
+
+static inline bool validate_observer(
+	struct observer *obs, const struct memvalue *now)
+{
+	/* evaluation of current state and compare to old one */
+	const unsigned active = memvalue_active(&obs->threshold, now);
+
+	/*
+	 * If we evaluated status just before and did not send update
+	 * yet to user-space we must preserve update flag.
+	 */
+	if (active != obs->active) {
+		obs->active  = active;
+		obs->updated = true;
+	}
+
+	return obs->updated;
+}
+
+/* Please update this function if contents memtypes is changed */
+static inline void get_memory_status(struct memvalue *value)
+{
+	/* field #0 -- total available pages */
+	value->v[0] = available_pages;
+
+	/* field #1 -- used memory by substracting free memories */
+	value->v[1] = available_pages;
+
+	/* RAM part: free + slab rec + cached - shared - mlocked */
+	value->v[1] -= global_page_state(NR_FREE_PAGES);
+	value->v[1] -= global_page_state(NR_SLAB_RECLAIMABLE);
+	value->v[1] -= global_page_state(NR_FILE_PAGES);
+	value->v[1] += global_page_state(NR_SHMEM);
+	value->v[1] += global_page_state(NR_MLOCK);
+#ifdef CONFIG_SWAP
+	/* Swap if we have */
+	if (available_swap_pages) {
+		struct sysinfo si;
+		si_swapinfo(&si);
+		value->v[1] -= si.freeswap;
+	}
+#endif
+
+	/* field #2 -- active pages */
+	value->v[2]  = global_page_state(LRU_ACTIVE_FILE);
+	value->v[2] += global_page_state(LRU_ACTIVE_ANON);
+}
+
+/* this method invoked from timer to re-check statistics */
+static void timer_function(unsigned long data)
+{
+	/* data is not used */
+	data = data;
+
+	/* query current memory statistics */
+	get_memory_status(&last_measured);
+
+	/* do we have value changed? */
+	if (!memvalue_about_equal(&last_measured, &last_notified)) {
+		last_notified = last_measured;
+		update_timer_jiffies = update_period_jiffies;
+		if (atomic_read(&observer_counter) > 0) {
+			bool updated = false;
+			struct list_head *pos;
+
+			spin_lock(&observer_lock);
+			list_for_each(pos, &observer_list) {
+				struct observer *obs = (struct observer *)pos;
+				if (validate_observer(obs, &last_measured)) {
+					updated = true;
+					/*
+					 * some watcher changed status
+					 * the rest will be done in mn_poll
+					 */
+					break;
+				}
+			}
+			spin_unlock(&observer_lock);
+
+			if (updated) {
+				/*
+				 * Wakeup of tasks should happened rare, only
+				 * when at least one theshold changed. So has
+				 * sense to show this information in logs.
+				 */
+				dev_info(dev, "wakeup polling tasks\n");
+				wake_up_all(&watcher_queue);
+			}
+		}
+	} else {
+		update_timer_jiffies <<= 1;
+		if (update_timer_jiffies > max_update_period_jiffies)
+			update_timer_jiffies = max_update_period_jiffies;
+	}
+
+	dev_dbg(dev, "tick for %lu jiffies\n", update_timer_jiffies);
+	mod_timer(&timer, jiffies + update_timer_jiffies);
+}
+
+static int vm_shrink(struct shrinker *sh, struct shrink_control *sc)
+{
+	/* unused values */
+	sh = sh;
+	sc = sc;
+
+	/* we are in reclaim mode - recheck memory situation later */
+	if (update_timer_jiffies != update_period_jiffies) {
+		/* adjust timer only in case it has sense */
+		update_timer_jiffies = update_period_jiffies;
+		mod_timer(&timer, jiffies + update_timer_jiffies);
+		dev_dbg(dev, "memory pressure - timer adjusted\n");
+	}
+
+	return 0;
+}
+
+static struct shrinker vm_shrinker = {
+	.shrink = vm_shrink,
+	.seeks = DEFAULT_SEEKS
+};
+
+
+static inline void subscribe_vm_stats(void)
+{
+	/* the initial delay always started from specified value */
+	update_timer_jiffies = update_period_jiffies;
+
+	/* update memory statistics */
+	get_memory_status(&last_measured);
+	last_notified = last_measured;
+
+	/* create timer and register shrinker */
+	init_timer_deferrable(&timer);
+	timer.data = 0;
+	timer.function = timer_function;
+	timer.expires = jiffies + update_timer_jiffies;
+	add_timer(&timer);
+	register_shrinker(&vm_shrinker);
+}
+
+static inline void unsubscribe_vm_stats(void)
+{
+	unregister_shrinker(&vm_shrinker);
+	del_timer_sync(&timer);
+}
+
+static int mn_open(struct inode *inode, struct file *file)
+{
+	struct observer *obs;
+
+	obs = kmalloc(sizeof(*obs), GFP_KERNEL);
+	if (obs) {
+		get_device(dev);
+
+		/* object initialization */
+		memset(obs, 0, sizeof(*obs));
+		obs->file      = file;
+		file->private_data = obs;
+
+		/* place it into checking list */
+		spin_lock(&observer_lock);
+		list_add(&obs->list, &observer_list);
+		spin_unlock(&observer_lock);
+
+		/* subscribe to vm stat updates */
+		if (1 == atomic_add_return(1, &observer_counter))
+			subscribe_vm_stats();
+
+		dev_dbg(dev, "0x%p - observer %u created\n",
+				obs, atomic_read(&observer_counter));
+
+		return 0;
+	}
+
+	return -ENOMEM;
+}
+
+static int mn_release(struct inode *inode, struct file *file)
+{
+	struct observer *obs = (struct observer *)file->private_data;
+
+	if (obs) {
+		dev_dbg(dev, "0x%p - observer released\n", obs);
+
+		/* unsubscribe from vm stat updates */
+		if (0 == atomic_sub_return(1, &observer_counter))
+			unsubscribe_vm_stats();
+
+		/* remove from checking list */
+		spin_lock(&observer_lock);
+		list_del(&obs->list);
+		spin_unlock(&observer_lock);
+
+		/* cleanup the memory */
+		file->private_data = NULL;
+		kfree(obs);
+
+		put_device(dev);
+	}
+
+	return 0;
+}
+
+static ssize_t mn_read(struct file *file, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	char tmp[MN_LINE_BUFFER_SIZE];
+	ssize_t pos = 0;
+	const struct memvalue mv = last_measured;
+	int idx;
+
+	for (idx = 0; idx < MN_TYPES_SIZE && pos < sizeof(tmp) - 2; idx++) {
+		ssize_t retval;
+
+		if (pos > 0)
+			tmp[pos++] = ' ';
+		retval = snprintf(&tmp[pos], sizeof(tmp) - pos,
+				"%s %lu",
+				memtypes[idx], mv.v[idx]);
+		if (retval > 0)
+			pos += retval;
+		else
+			return -EINVAL;
+	}
+
+	if (pos < sizeof(tmp))
+		tmp[pos++] = '\n';
+
+	if (pos > count)
+		pos = count;
+
+	return copy_to_user(buf, tmp, pos) ? -EINVAL : pos;
+}
+
+static ssize_t mn_write(struct file *file, const char __user *buf,
+					size_t count, loff_t *offset)
+{
+	struct observer *obs = (struct observer *)file->private_data;
+	char tmp[MN_LINE_BUFFER_SIZE];
+	ssize_t retval = min(count, sizeof(tmp) - 1);
+	int index;
+
+	if (copy_from_user(tmp, buf, retval))
+		return -EINVAL;
+
+	tmp[retval]  = 0;
+	obs->updated = false;
+	obs->active  = 0;
+	for (index = 0; index < MN_TYPES_SIZE; index++) {
+		const char *ptr = strstr(tmp, memtypes[index]);
+		if (ptr) {
+			char nmb[64];
+			int  i;
+
+			ptr += strlen(memtypes[index]) + 1;
+			while (*ptr && *ptr < '0')
+				ptr++;
+			for (i = 0; i < sizeof(nmb) - 1; i++, ptr++) {
+				const char c = *ptr;
+				if (c < '0' || c > '9')
+					break;
+				nmb[i] = c;
+			}
+			nmb[i] = 0;
+
+			if (kstrtoul(nmb, 10, &obs->threshold.v[index]) < 0) {
+				dev_dbg(dev,
+					"0x%p - cannot parse '%s' as '%s'\n",
+						obs, memtypes[index], nmb);
+				obs->threshold.v[index] = 0;
+				return -EINVAL;
+			}
+		} else
+			obs->threshold.v[index] = 0;
+	}
+	obs->active = memvalue_active(&obs->threshold, &last_measured);
+	dev_dbg(dev, "0x%p - threshold set to 0x%x\n", obs, obs->active);
+
+	return (ssize_t)count;
+}
+
+static unsigned int mn_poll(struct file *file, poll_table *wait)
+{
+	struct observer *obs = (struct observer *)file->private_data;
+
+	if (NULL == obs)
+		return 0;
+
+	poll_wait(file, &watcher_queue, wait);
+	if (validate_observer(obs, &last_measured)) {
+		dev_info(dev, "0x%p - threshold updated to 0x%x\n",
+					obs, obs->active);
+		obs->updated = false;
+		return POLLIN;
+	} else
+		return 0;
+}
+
+
+
+static const struct file_operations mn_fops = {
+	.owner   = THIS_MODULE,
+	.llseek  = noop_llseek,
+	.open    = mn_open,
+	.release = mn_release,
+	.read    = mn_read,
+	.write   = mn_write,
+	.poll    = mn_poll,
+};
+
+static struct miscdevice mn_device = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name  = "memnotify",
+	.fops  = &mn_fops,
+};
+
+
+static int __init mn_init(void)
+{
+	struct sysinfo si;
+	int error;
+#ifdef DEBUG
+	int i;
+#endif
+
+	error = misc_register(&mn_device);
+	if (error) {
+		pr_err("unable to register device %d\n", error);
+		return error;
+	}
+	dev = mn_device.this_device;
+
+	update_period_jiffies = msecs_to_jiffies(update_period);
+	if (!update_period_jiffies)
+		update_period_jiffies = msecs_to_jiffies(MN_UPDATE_PERIOD);
+
+	max_update_period_jiffies = msecs_to_jiffies(max_update_period);
+	if (max_update_period_jiffies < update_period_jiffies)
+		max_update_period_jiffies = update_period_jiffies;
+
+	/* query amount of available ram and swap, mem_unit is PAGE_SIZE */
+	si_meminfo(&si);
+#ifdef CONFIG_SWAP
+	si_swapinfo(&si);
+	available_pages = si.totalram + si.totalswap;
+	available_swap_pages = si.totalswap;
+#else
+	available_pages = si.totalram;
+#endif
+	/* if autodetect then set granularity to ~1.4% from available memory */
+	/* update_space_pages extra divided by 2 due to it is an offset      */
+	if (update_space)
+		update_space_pages = update_space >> (PAGE_SHIFT - 10 + 1);
+	else
+		update_space_pages = available_pages >> 7;
+	if (!update_space_pages)
+		update_space_pages = MN_UPDATE_SPACE >> (PAGE_SHIFT - 10 + 1);
+
+	dev_dbg(dev, "update period set to %u ms or %lu jiffies\n",
+				update_period, update_period_jiffies);
+	dev_dbg(dev, "update period limit set to %u ms or %lu jiffies\n",
+				max_update_period, max_update_period_jiffies);
+	dev_dbg(dev, "update space set to %u kb or -+%u pages\n",
+				update_space, update_space_pages);
+
+#ifdef CONFIG_SWAP
+	dev_dbg(dev, "%lu available pages found (%lu ram + %lu swap)\n",
+				available_pages, si.totalram, si.totalswap);
+#else
+	dev_dbg(dev, "%lu available pages found (only ram)\n",
+						available_pages);
+#endif
+
+#ifdef DEBUG
+	get_memory_status(&last_measured);
+	for (i = 0; i < MN_TYPES_SIZE; i++) {
+		dev_dbg(dev, "%lu %s pages, utilization %lu percents\n",
+						last_measured.v[i],
+							memtypes[i],
+			(100 * last_measured.v[i]) / available_pages);
+	}
+#endif
+
+	dev_dbg(dev, "overhead per client connection is %u bytes\n",
+				(unsigned)sizeof(struct observer));
+
+	return 0;
+}
+
+static void __exit mn_exit(void)
+{
+	dev = NULL;
+	misc_deregister(&mn_device);
+}
+
+
+module_init(mn_init);
+module_exit(mn_exit);
-- 
1.7.7.3


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/2] Memory notification pseudo-device module
  2012-01-17 13:22 ` [PATCH v2 2/2] Memory notification pseudo-device module Leonid Moiseichuk
@ 2012-01-17 13:32   ` Pekka Enberg
  2012-01-17 13:45     ` leonid.moiseichuk
  0 siblings, 1 reply; 14+ messages in thread
From: Pekka Enberg @ 2012-01-17 13:32 UTC (permalink / raw)
  To: Leonid Moiseichuk
  Cc: linux-mm, linux-kernel, cesarb, kamezawa.hiroyu, emunson,
	aarcange, riel, mel, rientjes, dima, gregkh, rebecca, san, akpm,
	vesa.jaaskelainen

On Tue, Jan 17, 2012 at 3:22 PM, Leonid Moiseichuk
<leonid.moiseichuk@nokia.com> wrote:
> The memory notification (memnotify) device tracks level of memory utilization,
> active page set and notifies subscribed processes when consumption crossed
> specified threshold(s) up or down. It could be used on embedded devices to
> implementation of performance-cheap memory reacting by using
> e.g. libmemnotify or similar user-space component.
>
> The minimal (250 ms) and maximal (15s) periods of reaction and granularity
> (~1.4% of memory size) could be tuned using module options.
>
> Signed-off-by: Leonid Moiseichuk <leonid.moiseichuk@nokia.com>

Is the point of making this a misc device to keep the ABI compatible
with N9? Is the ABI documented?

                        Pekka

^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [PATCH v2 2/2] Memory notification pseudo-device module
  2012-01-17 13:32   ` Pekka Enberg
@ 2012-01-17 13:45     ` leonid.moiseichuk
  2012-01-17 13:58       ` Pekka Enberg
  0 siblings, 1 reply; 14+ messages in thread
From: leonid.moiseichuk @ 2012-01-17 13:45 UTC (permalink / raw)
  To: penberg
  Cc: linux-mm, linux-kernel, cesarb, kamezawa.hiroyu, emunson,
	aarcange, riel, mel, rientjes, dima, gregkh, rebecca, san, akpm,
	vesa.jaaskelainen

Not only, here is many reasons:
1. publish code for review by people who knows how to do things and who can advise something valuable
2. to have one more update source in addition to memcg I used in n9 for libmemnotify and which works not I like to see (ideally drop memcg)
3. maybe someone needs similar solution, keep it internally = kill it. Now module looks pretty simple for me and maintainable. Plus one small issue fixed for swapinfo()

So at least now it could be used for tracking activity and it is a good improvement. It also could be extended to report "memory pressure value" similar to Minchan's patch does if necessary.

With Best Wishes,
Leonid


> -----Original Message-----
> From: penberg@gmail.com [mailto:penberg@gmail.com] On Behalf Of ext
> Pekka Enberg
> Sent: 17 January, 2012 15:33
> To: Moiseichuk Leonid (Nokia-MP/Helsinki)
> Cc: linux-mm@kvack.org; linux-kernel@vger.kernel.org; cesarb@cesarb.net;
> kamezawa.hiroyu@jp.fujitsu.com; emunson@mgebm.net;
> aarcange@redhat.com; riel@redhat.com; mel@csn.ul.ie;
> rientjes@google.com; dima@android.com; gregkh@suse.de;
> rebecca@android.com; san@google.com; akpm@linux-foundation.org;
> Jaaskelainen Vesa (Nokia-MP/Helsinki)
> Subject: Re: [PATCH v2 2/2] Memory notification pseudo-device module
> 
> On Tue, Jan 17, 2012 at 3:22 PM, Leonid Moiseichuk
> <leonid.moiseichuk@nokia.com> wrote:
> > The memory notification (memnotify) device tracks level of memory
> > utilization, active page set and notifies subscribed processes when
> > consumption crossed specified threshold(s) up or down. It could be
> > used on embedded devices to implementation of performance-cheap
> memory
> > reacting by using e.g. libmemnotify or similar user-space component.
> >
> > The minimal (250 ms) and maximal (15s) periods of reaction and
> > granularity (~1.4% of memory size) could be tuned using module options.
> >
> > Signed-off-by: Leonid Moiseichuk <leonid.moiseichuk@nokia.com>
> 
> Is the point of making this a misc device to keep the ABI compatible with N9?
> Is the ABI documented?
> 
>                         Pekka

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 2/2] Memory notification pseudo-device module
  2012-01-17 13:45     ` leonid.moiseichuk
@ 2012-01-17 13:58       ` Pekka Enberg
  2012-01-17 14:28         ` leonid.moiseichuk
  0 siblings, 1 reply; 14+ messages in thread
From: Pekka Enberg @ 2012-01-17 13:58 UTC (permalink / raw)
  To: leonid.moiseichuk
  Cc: linux-mm, linux-kernel, cesarb, kamezawa.hiroyu, emunson,
	aarcange, riel, mel, rientjes, dima, gregkh, rebecca, san, akpm,
	vesa.jaaskelainen

On Tue, Jan 17, 2012 at 3:45 PM,  <leonid.moiseichuk@nokia.com> wrote:
> 3. maybe someone needs similar solution, keep it internally = kill it. Now
> module looks pretty simple for me and maintainable. Plus one small issue
> fixed for swapinfo()

If you're serious about making this a generic thing, it must live in
mm/mem_notify.c. No ifs or buts about it.

I'm also not completely convinced we need to put memnotify policy in
the kernel. Why can't we extend Minchan's patch to report the relevant
numbers and let the userspace figure out when pressure is above some
interesting threshold?

                                Pekka

^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [PATCH v2 2/2] Memory notification pseudo-device module
  2012-01-17 13:58       ` Pekka Enberg
@ 2012-01-17 14:28         ` leonid.moiseichuk
  0 siblings, 0 replies; 14+ messages in thread
From: leonid.moiseichuk @ 2012-01-17 14:28 UTC (permalink / raw)
  To: penberg
  Cc: linux-mm, linux-kernel, cesarb, kamezawa.hiroyu, emunson,
	aarcange, riel, mel, rientjes, dima, gregkh, rebecca, san, akpm,
	vesa.jaaskelainen

> -----Original Message-----
> From: penberg@gmail.com [mailto:penberg@gmail.com] On Behalf Of ext
> Pekka Enberg
> Sent: 17 January, 2012 15:59
...
> If you're serious about making this a generic thing, it must live in
> mm/mem_notify.c. No ifs or buts about it.
> I'm also not completely convinced we need to put memnotify policy in the
> kernel. Why can't we extend Minchan's patch to report the relevant
> numbers and let the userspace figure out when pressure is above some
> interesting threshold?

I do not insist to have it as a part mm, but if you have 1-2-3 items what should be done in this or Minchan's patch I can participate.
>From my point of view Minchan's patch is not ideal due to required:
1. depends on cgroups (at least as I see it from patch in shrink_mem_cgroup_zone()part)
2. reports only memory pressure based on relation in between free and file pages which is means by active file IO you may get lowmem 
3. swapping should not produce lowmem, but active swapping - must, is  it checked there?
+	if (nr[LRU_INACTIVE_ANON])
+		low_mem = true;
4. required changes in vmscan

I think, due to everyone based on his experience/working area profile has own understanding what is "low memory"  (or similar situation which needs to be tracked) 
it should be  some generic or extendable API, not like ON/OFF trigger for something happened inside VM. From another point of view it should not be too generic due 
to tasks could be solved using memcg, ionice, OOM killer or variations of soft-OOM-patches.




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 1/2] Making si_swapinfo exportable
  2012-01-17 13:22 ` [PATCH v2 1/2] Making si_swapinfo exportable Leonid Moiseichuk
@ 2012-01-18 10:34   ` Pekka Enberg
  2012-01-18 14:09     ` Greg KH
  0 siblings, 1 reply; 14+ messages in thread
From: Pekka Enberg @ 2012-01-18 10:34 UTC (permalink / raw)
  To: Leonid Moiseichuk
  Cc: linux-mm, linux-kernel, cesarb, kamezawa.hiroyu, emunson,
	aarcange, riel, mel, rientjes, dima, gregkh, rebecca, san, akpm,
	vesa.jaaskelainen

On Tue, Jan 17, 2012 at 3:22 PM, Leonid Moiseichuk
<leonid.moiseichuk@nokia.com> wrote:
> If we will make si_swapinfo() exportable it could be called from modules.
> Otherwise modules have no interface to obtain information about swap usage.
> Change made in the same way as si_meminfo() declared.
>
> Signed-off-by: Leonid Moiseichuk <leonid.moiseichuk@nokia.com>
> ---
>  mm/swapfile.c |    3 +++
>  1 files changed, 3 insertions(+), 0 deletions(-)
>
> diff --git a/mm/swapfile.c b/mm/swapfile.c
> index b1cd120..192cc25 100644
> --- a/mm/swapfile.c
> +++ b/mm/swapfile.c
> @@ -5,10 +5,12 @@
>  *  Swap reorganised 29.12.95, Stephen Tweedie
>  */
>
> +#include <linux/export.h>
>  #include <linux/mm.h>
>  #include <linux/hugetlb.h>
>  #include <linux/mman.h>
>  #include <linux/slab.h>
> +#include <linux/kernel.h>
>  #include <linux/kernel_stat.h>
>  #include <linux/swap.h>
>  #include <linux/vmalloc.h>
> @@ -2177,6 +2179,7 @@ void si_swapinfo(struct sysinfo *val)
>        val->totalswap = total_swap_pages + nr_to_be_unused;
>        spin_unlock(&swap_lock);
>  }
> +EXPORT_SYMBOL(si_swapinfo);

FWIW, I'm completely OK with this export:

  Acked-by: Pekka Enberg <penberg@kernel.org>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 1/2] Making si_swapinfo exportable
  2012-01-18 10:34   ` Pekka Enberg
@ 2012-01-18 14:09     ` Greg KH
  2012-01-18 14:46       ` Konrad Rzeszutek Wilk
  2012-01-18 14:47       ` leonid.moiseichuk
  0 siblings, 2 replies; 14+ messages in thread
From: Greg KH @ 2012-01-18 14:09 UTC (permalink / raw)
  To: Pekka Enberg
  Cc: Leonid Moiseichuk, linux-mm, linux-kernel, cesarb,
	kamezawa.hiroyu, emunson, aarcange, riel, mel, rientjes, dima,
	rebecca, san, akpm, vesa.jaaskelainen

On Wed, Jan 18, 2012 at 12:34:19PM +0200, Pekka Enberg wrote:
> On Tue, Jan 17, 2012 at 3:22 PM, Leonid Moiseichuk
> <leonid.moiseichuk@nokia.com> wrote:
> > If we will make si_swapinfo() exportable it could be called from modules.
> > Otherwise modules have no interface to obtain information about swap usage.
> > Change made in the same way as si_meminfo() declared.
> >
> > Signed-off-by: Leonid Moiseichuk <leonid.moiseichuk@nokia.com>
> > ---
> >  mm/swapfile.c |    3 +++
> >  1 files changed, 3 insertions(+), 0 deletions(-)
> >
> > diff --git a/mm/swapfile.c b/mm/swapfile.c
> > index b1cd120..192cc25 100644
> > --- a/mm/swapfile.c
> > +++ b/mm/swapfile.c
> > @@ -5,10 +5,12 @@
> >  *  Swap reorganised 29.12.95, Stephen Tweedie
> >  */
> >
> > +#include <linux/export.h>
> >  #include <linux/mm.h>
> >  #include <linux/hugetlb.h>
> >  #include <linux/mman.h>
> >  #include <linux/slab.h>
> > +#include <linux/kernel.h>
> >  #include <linux/kernel_stat.h>
> >  #include <linux/swap.h>
> >  #include <linux/vmalloc.h>
> > @@ -2177,6 +2179,7 @@ void si_swapinfo(struct sysinfo *val)
> >        val->totalswap = total_swap_pages + nr_to_be_unused;
> >        spin_unlock(&swap_lock);
> >  }
> > +EXPORT_SYMBOL(si_swapinfo);

EXPORT_SYMBOL_GPL() perhaps?

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 1/2] Making si_swapinfo exportable
  2012-01-18 14:09     ` Greg KH
@ 2012-01-18 14:46       ` Konrad Rzeszutek Wilk
  2012-01-18 15:00         ` Greg KH
  2012-01-18 14:47       ` leonid.moiseichuk
  1 sibling, 1 reply; 14+ messages in thread
From: Konrad Rzeszutek Wilk @ 2012-01-18 14:46 UTC (permalink / raw)
  To: Greg KH
  Cc: Pekka Enberg, Leonid Moiseichuk, linux-mm, linux-kernel, cesarb,
	kamezawa.hiroyu, emunson, aarcange, riel, mel, rientjes, dima,
	rebecca, san, akpm, vesa.jaaskelainen

> > > +EXPORT_SYMBOL(si_swapinfo);
> 
> EXPORT_SYMBOL_GPL() perhaps?

Greg,

So.. could you tell when are suppose to do _GPL and when not? Is there
a policy of "new code must be _GPL" ? Or is there some extra "if .. then"
conditions?

Thanks!

^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [PATCH v2 1/2] Making si_swapinfo exportable
  2012-01-18 14:09     ` Greg KH
  2012-01-18 14:46       ` Konrad Rzeszutek Wilk
@ 2012-01-18 14:47       ` leonid.moiseichuk
  2012-01-18 15:00         ` Greg KH
  1 sibling, 1 reply; 14+ messages in thread
From: leonid.moiseichuk @ 2012-01-18 14:47 UTC (permalink / raw)
  To: gregkh, penberg
  Cc: linux-mm, linux-kernel, cesarb, kamezawa.hiroyu, emunson,
	aarcange, riel, mel, rientjes, dima, rebecca, san, akpm,
	vesa.jaaskelainen

> -----Original Message-----
> From: ext Greg KH [mailto:gregkh@suse.de]
> Sent: 18 January, 2012 16:09
...

> > > +EXPORT_SYMBOL(si_swapinfo);
> 
> EXPORT_SYMBOL_GPL() perhaps?

I followed si_meminfo which is uses EXPORT_SYMBOL. 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 1/2] Making si_swapinfo exportable
  2012-01-18 14:46       ` Konrad Rzeszutek Wilk
@ 2012-01-18 15:00         ` Greg KH
  0 siblings, 0 replies; 14+ messages in thread
From: Greg KH @ 2012-01-18 15:00 UTC (permalink / raw)
  To: Konrad Rzeszutek Wilk
  Cc: Pekka Enberg, Leonid Moiseichuk, linux-mm, linux-kernel, cesarb,
	kamezawa.hiroyu, emunson, aarcange, riel, mel, rientjes, dima,
	rebecca, san, akpm, vesa.jaaskelainen

On Wed, Jan 18, 2012 at 09:46:18AM -0500, Konrad Rzeszutek Wilk wrote:
> > > > +EXPORT_SYMBOL(si_swapinfo);
> > 
> > EXPORT_SYMBOL_GPL() perhaps?
> 
> Greg,
> 
> So.. could you tell when are suppose to do _GPL and when not? Is there
> a policy of "new code must be _GPL" ? Or is there some extra "if .. then"
> conditions?

It's up to the author of the code, what their preference is.

I just generally prefer _GPL for new exports.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 1/2] Making si_swapinfo exportable
  2012-01-18 14:47       ` leonid.moiseichuk
@ 2012-01-18 15:00         ` Greg KH
  2012-01-18 15:20           ` Pekka Enberg
  0 siblings, 1 reply; 14+ messages in thread
From: Greg KH @ 2012-01-18 15:00 UTC (permalink / raw)
  To: leonid.moiseichuk
  Cc: penberg, linux-mm, linux-kernel, cesarb, kamezawa.hiroyu,
	emunson, aarcange, riel, mel, rientjes, dima, rebecca, san, akpm,
	vesa.jaaskelainen

On Wed, Jan 18, 2012 at 02:47:47PM +0000, leonid.moiseichuk@nokia.com wrote:
> > -----Original Message-----
> > From: ext Greg KH [mailto:gregkh@suse.de]
> > Sent: 18 January, 2012 16:09
> ...
> 
> > > > +EXPORT_SYMBOL(si_swapinfo);
> > 
> > EXPORT_SYMBOL_GPL() perhaps?
> 
> I followed si_meminfo which is uses EXPORT_SYMBOL.

Ah, good point.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v2 1/2] Making si_swapinfo exportable
  2012-01-18 15:00         ` Greg KH
@ 2012-01-18 15:20           ` Pekka Enberg
  0 siblings, 0 replies; 14+ messages in thread
From: Pekka Enberg @ 2012-01-18 15:20 UTC (permalink / raw)
  To: Greg KH
  Cc: leonid.moiseichuk, linux-mm, linux-kernel, cesarb,
	kamezawa.hiroyu, emunson, aarcange, riel, mel, rientjes, dima,
	rebecca, san, akpm, vesa.jaaskelainen

On Wed, 2012-01-18 at 07:00 -0800, Greg KH wrote:
> On Wed, Jan 18, 2012 at 02:47:47PM +0000, leonid.moiseichuk@nokia.com wrote:
> > > -----Original Message-----
> > > From: ext Greg KH [mailto:gregkh@suse.de]
> > > Sent: 18 January, 2012 16:09
> > ...
> > 
> > > > > +EXPORT_SYMBOL(si_swapinfo);
> > > 
> > > EXPORT_SYMBOL_GPL() perhaps?
> > 
> > I followed si_meminfo which is uses EXPORT_SYMBOL.
> 
> Ah, good point.

Yup, I think EXPORT_SYMBOL is appropriate here.


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2012-01-18 15:20 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-01-17 13:22 [PATCH v2 0/2] Memory notification pseudo-device module Leonid Moiseichuk
2012-01-17 13:22 ` [PATCH v2 1/2] Making si_swapinfo exportable Leonid Moiseichuk
2012-01-18 10:34   ` Pekka Enberg
2012-01-18 14:09     ` Greg KH
2012-01-18 14:46       ` Konrad Rzeszutek Wilk
2012-01-18 15:00         ` Greg KH
2012-01-18 14:47       ` leonid.moiseichuk
2012-01-18 15:00         ` Greg KH
2012-01-18 15:20           ` Pekka Enberg
2012-01-17 13:22 ` [PATCH v2 2/2] Memory notification pseudo-device module Leonid Moiseichuk
2012-01-17 13:32   ` Pekka Enberg
2012-01-17 13:45     ` leonid.moiseichuk
2012-01-17 13:58       ` Pekka Enberg
2012-01-17 14:28         ` leonid.moiseichuk

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).