* [PATCH v3 4/6] power: max17040: Add ID for MAX77836 Fuel Gauge block
From: Krzysztof Kozlowski @ 2014-07-04 10:12 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov, David Woodhouse, linux-kernel, linux-api
Cc: Samuel Ortiz, Lee Jones, Liam Girdwood, Mark Brown, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Kyungmin Park,
Marek Szyprowski, Bartlomiej Zolnierkiewicz, Krzysztof Kozlowski,
Anton Vorontsov
In-Reply-To: <1404468765-10818-1-git-send-email-k.kozlowski@samsung.com>
MAX77836 has the same Fuel Gauge as MAX17040/17048. The max17040 driver
can be safely re-used. The patch adds MAX77836 ID to array of
i2c_device_id.
Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Anton Vorontsov <anton@enomsg.org>
Cc: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
---
drivers/power/max17040_battery.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/power/max17040_battery.c b/drivers/power/max17040_battery.c
index 0fbac861080d..165ffe381803 100644
--- a/drivers/power/max17040_battery.c
+++ b/drivers/power/max17040_battery.c
@@ -278,6 +278,7 @@ static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);
static const struct i2c_device_id max17040_id[] = {
{ "max17040", 0 },
+ { "max77836-battery", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max17040_id);
--
1.9.1
^ permalink raw reply related
* [PATCH v3 3/6] charger: max14577: Configure battery-dependent settings from DTS and sysfs
From: Krzysztof Kozlowski @ 2014-07-04 10:12 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov, David Woodhouse,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA
Cc: Samuel Ortiz, Lee Jones, Liam Girdwood, Mark Brown, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Kyungmin Park,
Marek Szyprowski, Bartlomiej Zolnierkiewicz, Krzysztof Kozlowski,
Jenny Tc
In-Reply-To: <1404468765-10818-1-git-send-email-k.kozlowski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Remove hard-coded values for:
- Fast Charge current,
- End Of Charge current,
- Fast Charge timer,
- Overvoltage Protection Threshold,
- Battery Constant Voltage,
and use DTS or sysfs to configure them. This allows using the max14577 charger
driver with different batteries.
Now the charger driver requires valid configuration data from DTS. In
case of wrong configuration data it fails during probe. Patch adds
of_compatible to the charger mfd cell in MFD driver core.
The fast charge timer is configured through sysfs entry.
Signed-off-by: Krzysztof Kozlowski <k.kozlowski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Cc: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Cc: Dmitry Eremin-Solenikov <dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: David Woodhouse <dwmw2-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>
Cc: Jenny Tc <jenny.tc-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
Acked-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
drivers/mfd/max14577.c | 5 +-
drivers/power/Kconfig | 1 +
drivers/power/max14577_charger.c | 311 +++++++++++++++++++++++++++++++----
include/linux/mfd/max14577-private.h | 19 +++
include/linux/mfd/max14577.h | 7 +
5 files changed, 314 insertions(+), 29 deletions(-)
diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c
index e6f25aa0ded8..b8af263be594 100644
--- a/drivers/mfd/max14577.c
+++ b/drivers/mfd/max14577.c
@@ -116,7 +116,10 @@ static const struct mfd_cell max14577_devs[] = {
.name = "max14577-regulator",
.of_compatible = "maxim,max14577-regulator",
},
- { .name = "max14577-charger", },
+ {
+ .name = "max14577-charger",
+ .of_compatible = "maxim,max14577-charger",
+ },
};
static const struct mfd_cell max77836_devs[] = {
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 94086a5238c6..6c152f494c4a 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -320,6 +320,7 @@ config CHARGER_MANAGER
config CHARGER_MAX14577
tristate "Maxim MAX14577/77836 battery charger driver"
depends on MFD_MAX14577
+ select SYSFS
help
Say Y to enable support for the battery charger control sysfs and
platform data of MAX14577/77836 MUICs.
diff --git a/drivers/power/max14577_charger.c b/drivers/power/max14577_charger.c
index 19c8f42abf24..c125756eab69 100644
--- a/drivers/power/max14577_charger.c
+++ b/drivers/power/max14577_charger.c
@@ -19,6 +19,7 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/mfd/max14577-private.h>
+#include <linux/mfd/max14577.h>
struct max14577_charger {
struct device *dev;
@@ -27,6 +28,8 @@ struct max14577_charger {
unsigned int charging_state;
unsigned int battery_state;
+
+ struct max14577_charger_platform_data *pdata;
};
/*
@@ -178,15 +181,131 @@ static int max14577_get_present(struct max14577_charger *chg)
return 1;
}
+static inline int max14577_set_fast_charge_timer(struct max14577_charger *chg,
+ unsigned long hours)
+{
+ u8 reg_data;
+
+ switch (hours) {
+ case 5 ... 7:
+ reg_data = hours - 3;
+ break;
+ case 0:
+ /* Disable */
+ reg_data = 0x7;
+ break;
+ default:
+ dev_err(chg->dev, "Wrong value for Fast-Charge Timer: %lu\n",
+ hours);
+ return -EINVAL;
+ }
+ reg_data <<= CHGCTRL1_TCHW_SHIFT;
+
+ return max14577_update_reg(chg->max14577->regmap,
+ MAX14577_REG_CHGCTRL1, CHGCTRL1_TCHW_MASK, reg_data);
+}
+
+static inline int max14577_init_constant_voltage(struct max14577_charger *chg,
+ unsigned int uvolt)
+{
+ u8 reg_data;
+
+ if (uvolt < MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN ||
+ uvolt > MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX)
+ return -EINVAL;
+
+ if (uvolt == 4200000)
+ reg_data = 0x0;
+ else if (uvolt == MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX)
+ reg_data = 0x1f;
+ else if (uvolt <= 4280000) {
+ unsigned int val = uvolt;
+
+ val -= MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN;
+ val /= MAXIM_CHARGER_CONSTANT_VOLTAGE_STEP;
+ if (uvolt <= 4180000)
+ reg_data = 0x1 + val;
+ else
+ reg_data = val; /* Fix for gap between 4.18V and 4.22V */
+ } else
+ return -EINVAL;
+
+ reg_data <<= CHGCTRL3_MBCCVWRC_SHIFT;
+
+ return max14577_write_reg(chg->max14577->regmap,
+ MAX14577_CHG_REG_CHG_CTRL3, reg_data);
+}
+
+static inline int max14577_init_eoc(struct max14577_charger *chg,
+ unsigned int uamp)
+{
+ unsigned int current_bits = 0xf;
+ u8 reg_data;
+
+ switch (chg->max14577->dev_type) {
+ case MAXIM_DEVICE_TYPE_MAX77836:
+ if (uamp < 5000)
+ return -EINVAL; /* Requested current is too low */
+
+ if (uamp >= 7500 && uamp < 10000)
+ current_bits = 0x0;
+ else if (uamp <= 50000) {
+ /* <5000, 7499> and <10000, 50000> */
+ current_bits = uamp / 5000;
+ } else {
+ uamp = min(uamp, 100000U) - 50000U;
+ current_bits = 0xa + uamp / 10000;
+ }
+ break;
+
+ case MAXIM_DEVICE_TYPE_MAX14577:
+ default:
+ if (uamp < MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN)
+ return -EINVAL; /* Requested current is too low */
+
+ uamp = min(uamp, MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX);
+ uamp -= MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN;
+ current_bits = uamp / MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP;
+ break;
+ }
+
+ reg_data = current_bits << CHGCTRL5_EOCS_SHIFT;
+
+ return max14577_update_reg(chg->max14577->regmap,
+ MAX14577_CHG_REG_CHG_CTRL5, CHGCTRL5_EOCS_MASK,
+ reg_data);
+}
+
+static inline int max14577_init_fast_charge(struct max14577_charger *chg,
+ unsigned int uamp)
+{
+ u8 reg_data;
+ int ret;
+ const struct maxim_charger_current *limits =
+ &maxim_charger_currents[chg->max14577->dev_type];
+
+ ret = maxim_charger_calc_reg_current(limits, uamp, uamp, ®_data);
+ if (ret) {
+ dev_err(chg->dev, "Wrong value for fast charge: %u\n", uamp);
+ return ret;
+ }
+
+ return max14577_update_reg(chg->max14577->regmap,
+ MAX14577_CHG_REG_CHG_CTRL4,
+ CHGCTRL4_MBCICHWRCL_MASK | CHGCTRL4_MBCICHWRCH_MASK,
+ reg_data);
+}
+
/*
* Sets charger registers to proper and safe default values.
* Some of these values are equal to defaults in MAX14577E
* data sheet but there are minor differences.
*/
-static void max14577_charger_reg_init(struct max14577_charger *chg)
+static int max14577_charger_reg_init(struct max14577_charger *chg)
{
struct regmap *rmap = chg->max14577->regmap;
u8 reg_data;
+ int ret;
/*
* Charger-Type Manual Detection, default off (set CHGTYPMAN to 0)
@@ -198,10 +317,6 @@ static void max14577_charger_reg_init(struct max14577_charger *chg)
CDETCTRL1_CHGDETEN_MASK | CDETCTRL1_CHGTYPMAN_MASK,
reg_data);
- /* Battery Fast-Charge Timer, set to: 6hrs */
- reg_data = 0x3 << CHGCTRL1_TCHW_SHIFT;
- max14577_write_reg(rmap, MAX14577_REG_CHGCTRL1, reg_data);
-
/*
* Wall-Adapter Rapid Charge, default on
* Battery-Charger, default on
@@ -210,32 +325,46 @@ static void max14577_charger_reg_init(struct max14577_charger *chg)
reg_data |= 0x1 << CHGCTRL2_MBCHOSTEN_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL2, reg_data);
- /* Battery-Charger Constant Voltage (CV) Mode, set to: 4.35V */
- reg_data = 0xf << CHGCTRL3_MBCCVWRC_SHIFT;
- max14577_write_reg(rmap, MAX14577_REG_CHGCTRL3, reg_data);
-
- /*
- * Fast Battery-Charge Current Low,
- * default 200-950mA (max14577) / 100-475mA (max77836)
- *
- * Fast Battery-Charge Current High,
- * set to 450mA (max14577) / 225mA (max77836)
- */
- reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
- reg_data |= 0x5 << CHGCTRL4_MBCICHWRCH_SHIFT;
- max14577_write_reg(rmap, MAX14577_REG_CHGCTRL4, reg_data);
-
- /* End-of-Charge Current, set to 50mA (max14577) / 7.5mA (max77836) */
- reg_data = 0x0 << CHGCTRL5_EOCS_SHIFT;
- max14577_write_reg(rmap, MAX14577_REG_CHGCTRL5, reg_data);
-
/* Auto Charging Stop, default off */
reg_data = 0x0 << CHGCTRL6_AUTOSTOP_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL6, reg_data);
- /* Overvoltage-Protection Threshold, set to 6.5V */
- reg_data = 0x2 << CHGCTRL7_OTPCGHCVS_SHIFT;
+ ret = max14577_init_constant_voltage(chg, chg->pdata->constant_uvolt);
+ if (ret)
+ return ret;
+
+ ret = max14577_init_eoc(chg, chg->pdata->eoc_uamp);
+ if (ret)
+ return ret;
+
+ ret = max14577_init_fast_charge(chg, chg->pdata->fast_charge_uamp);
+ if (ret)
+ return ret;
+
+ ret = max14577_set_fast_charge_timer(chg,
+ MAXIM_CHARGER_FAST_CHARGE_TIMER_DEFAULT);
+ if (ret)
+ return ret;
+
+ /* Initialize Overvoltage-Protection Threshold */
+ switch (chg->pdata->ovp_uvolt) {
+ case 7500000:
+ reg_data = 0x0;
+ break;
+ case 6000000:
+ case 6500000:
+ case 7000000:
+ reg_data = 0x1 + (chg->pdata->ovp_uvolt - 6000000) / 500000;
+ break;
+ default:
+ dev_err(chg->dev, "Wrong value for OVP: %u\n",
+ chg->pdata->ovp_uvolt);
+ return -EINVAL;
+ }
+ reg_data <<= CHGCTRL7_OTPCGHCVS_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL7, reg_data);
+
+ return 0;
}
/* Support property from charger */
@@ -295,6 +424,110 @@ static int max14577_charger_get_property(struct power_supply *psy,
return ret;
}
+#ifdef CONFIG_OF
+static struct max14577_charger_platform_data *max14577_charger_dt_init(
+ struct platform_device *pdev)
+{
+ struct max14577_charger_platform_data *pdata;
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ if (!np) {
+ dev_err(&pdev->dev, "No charger OF node\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ ret = of_property_read_u32(np, "maxim,constant-uvolt",
+ &pdata->constant_uvolt);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot parse maxim,constant-uvolt field from DT\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = of_property_read_u32(np, "maxim,fast-charge-uamp",
+ &pdata->fast_charge_uamp);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot parse maxim,fast-charge-uamp field from DT\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = of_property_read_u32(np, "maxim,eoc-uamp", &pdata->eoc_uamp);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot parse maxim,eoc-uamp field from DT\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = of_property_read_u32(np, "maxim,ovp-uvolt", &pdata->ovp_uvolt);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot parse maxim,ovp-uvolt field from DT\n");
+ return ERR_PTR(ret);
+ }
+
+ return pdata;
+}
+#else /* CONFIG_OF */
+static struct max14577_charger_platform_data *max14577_charger_dt_init(
+ struct platform_device *pdev)
+{
+ return NULL;
+}
+#endif /* CONFIG_OF */
+
+static ssize_t show_fast_charge_timer(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct max14577_charger *chg = dev_get_drvdata(dev);
+ u8 reg_data;
+ int ret;
+ unsigned int val;
+
+ ret = max14577_read_reg(chg->max14577->regmap, MAX14577_REG_CHGCTRL1,
+ ®_data);
+ if (ret)
+ return ret;
+
+ reg_data &= CHGCTRL1_TCHW_MASK;
+ reg_data >>= CHGCTRL1_TCHW_SHIFT;
+ switch (reg_data) {
+ case 0x2 ... 0x4:
+ val = reg_data + 3;
+ break;
+ case 0x7:
+ val = 0;
+ break;
+ default:
+ val = 5;
+ break;
+ }
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_fast_charge_timer(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct max14577_charger *chg = dev_get_drvdata(dev);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = max14577_set_fast_charge_timer(chg, val);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR(fast_charge_timer, S_IRUGO | S_IWUSR,
+ show_fast_charge_timer, store_fast_charge_timer);
+
static int max14577_charger_probe(struct platform_device *pdev)
{
struct max14577_charger *chg;
@@ -309,7 +542,13 @@ static int max14577_charger_probe(struct platform_device *pdev)
chg->dev = &pdev->dev;
chg->max14577 = max14577;
- max14577_charger_reg_init(chg);
+ chg->pdata = max14577_charger_dt_init(pdev);
+ if (IS_ERR_OR_NULL(chg->pdata))
+ return PTR_ERR(chg->pdata);
+
+ ret = max14577_charger_reg_init(chg);
+ if (ret)
+ return ret;
chg->charger.name = "max14577-charger",
chg->charger.type = POWER_SUPPLY_TYPE_BATTERY,
@@ -317,19 +556,35 @@ static int max14577_charger_probe(struct platform_device *pdev)
chg->charger.num_properties = ARRAY_SIZE(max14577_charger_props),
chg->charger.get_property = max14577_charger_get_property,
+ ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer);
+ if (ret) {
+ dev_err(&pdev->dev, "failed: create sysfs entry\n");
+ return ret;
+ }
+
ret = power_supply_register(&pdev->dev, &chg->charger);
if (ret) {
dev_err(&pdev->dev, "failed: power supply register\n");
- return ret;
+ goto err;
}
+ /* Check for valid values for charger */
+ BUILD_BUG_ON(MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN +
+ MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP * 0xf !=
+ MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX);
return 0;
+
+err:
+ device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
+
+ return ret;
}
static int max14577_charger_remove(struct platform_device *pdev)
{
struct max14577_charger *chg = platform_get_drvdata(pdev);
+ device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
power_supply_unregister(&chg->charger);
return 0;
diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h
index 7d514839c764..f01c1fae4d84 100644
--- a/include/linux/mfd/max14577-private.h
+++ b/include/linux/mfd/max14577-private.h
@@ -293,6 +293,25 @@ enum max14577_charger_reg {
#define MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP 25000U
#define MAX77836_CHARGER_CURRENT_LIMIT_MAX 475000U
+/*
+ * MAX14577 charger End-Of-Charge current limits
+ * (as in CHGCTRL5 register), uA
+ */
+#define MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN 50000U
+#define MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP 10000U
+#define MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX 200000U
+
+/*
+ * MAX14577/MAX77836 Battery Constant Voltage
+ * (as in CHGCTRL3 register), uV
+ */
+#define MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN 4000000U
+#define MAXIM_CHARGER_CONSTANT_VOLTAGE_STEP 20000U
+#define MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX 4350000U
+
+/* Default value for fast charge timer, in hours */
+#define MAXIM_CHARGER_FAST_CHARGE_TIMER_DEFAULT 5
+
/* MAX14577 regulator SFOUT LDO voltage, fixed, uV */
#define MAX14577_REGULATOR_SAFEOUT_VOLTAGE 4900000
diff --git a/include/linux/mfd/max14577.h b/include/linux/mfd/max14577.h
index 3c098d57b1d1..ccfaf952c31b 100644
--- a/include/linux/mfd/max14577.h
+++ b/include/linux/mfd/max14577.h
@@ -54,6 +54,13 @@ struct max14577_regulator_platform_data {
struct device_node *of_node;
};
+struct max14577_charger_platform_data {
+ u32 constant_uvolt;
+ u32 fast_charge_uamp;
+ u32 eoc_uamp;
+ u32 ovp_uvolt;
+};
+
/*
* MAX14577 MFD platform data
*/
--
1.9.1
^ permalink raw reply related
* [PATCH v3 2/6] regulator/mfd: max14577: Export symbols for calculating charger current
From: Krzysztof Kozlowski @ 2014-07-04 10:12 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov, David Woodhouse,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA
Cc: Samuel Ortiz, Lee Jones, Liam Girdwood, Mark Brown, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Kyungmin Park,
Marek Szyprowski, Bartlomiej Zolnierkiewicz, Krzysztof Kozlowski
In-Reply-To: <1404468765-10818-1-git-send-email-k.kozlowski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
This patch prepares for changing the max14577 charger driver to allow
configuring battery-dependent settings from DTS.
The patch moves from regulator driver to MFD core driver and exports:
- function for calculating register value for charger's current;
- table of limits for chargers (MAX14577, MAX77836).
Previously they were used only by the max14577 regulator driver. In next
patch the charger driver will use them as well. Exporting them will
reduce unnecessary code duplication.
Signed-off-by: Krzysztof Kozlowski <k.kozlowski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Cc: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Acked-by: Mark Brown <broonie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Acked-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
drivers/mfd/max14577.c | 95 ++++++++++++++++++++++++++++++++++++
drivers/regulator/max14577.c | 80 ++----------------------------
include/linux/mfd/max14577-private.h | 22 ++++-----
include/linux/mfd/max14577.h | 23 +++++++++
4 files changed, 133 insertions(+), 87 deletions(-)
diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c
index 4a5e885383f8..e6f25aa0ded8 100644
--- a/drivers/mfd/max14577.c
+++ b/drivers/mfd/max14577.c
@@ -26,6 +26,87 @@
#include <linux/mfd/max14577.h>
#include <linux/mfd/max14577-private.h>
+/*
+ * Table of valid charger currents for different Maxim chipsets.
+ * It is placed here because it is used by both charger and regulator driver.
+ */
+const struct maxim_charger_current maxim_charger_currents[] = {
+ [MAXIM_DEVICE_TYPE_UNKNOWN] = { 0, 0, 0, 0 },
+ [MAXIM_DEVICE_TYPE_MAX14577] = {
+ .min = MAX14577_CHARGER_CURRENT_LIMIT_MIN,
+ .high_start = MAX14577_CHARGER_CURRENT_LIMIT_HIGH_START,
+ .high_step = MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP,
+ .max = MAX14577_CHARGER_CURRENT_LIMIT_MAX,
+ },
+ [MAXIM_DEVICE_TYPE_MAX77836] = {
+ .min = MAX77836_CHARGER_CURRENT_LIMIT_MIN,
+ .high_start = MAX77836_CHARGER_CURRENT_LIMIT_HIGH_START,
+ .high_step = MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP,
+ .max = MAX77836_CHARGER_CURRENT_LIMIT_MAX,
+ },
+};
+EXPORT_SYMBOL_GPL(maxim_charger_currents);
+
+/*
+ * maxim_charger_calc_reg_current - Calculate register value for current
+ * @limits: constraints for charger, matching the MBCICHWRC register
+ * @min_ua: minimal requested current, micro Amps
+ * @max_ua: maximum requested current, micro Amps
+ * @dst: destination to store calculated register value
+ *
+ * Calculates the value of MBCICHWRC (Fast Battery Charge Current) register
+ * for given current and stores it under pointed 'dst'. The stored value
+ * combines low bit (MBCICHWRCL) and high bits (MBCICHWRCH). It is also
+ * properly shifted.
+ *
+ * The calculated register value matches the current which:
+ * - is always between <limits.min, limits.max>;
+ * - is always less or equal to max_ua;
+ * - is the highest possible value;
+ * - may be lower than min_ua.
+ *
+ * On success returns 0. On error returns -EINVAL (requested min/max current
+ * is outside of given charger limits) and 'dst' is not set.
+ */
+int maxim_charger_calc_reg_current(const struct maxim_charger_current *limits,
+ unsigned int min_ua, unsigned int max_ua, u8 *dst)
+{
+ unsigned int current_bits = 0xf;
+
+ if (min_ua > max_ua)
+ return -EINVAL;
+
+ if (min_ua > limits->max || max_ua < limits->min)
+ return -EINVAL;
+
+ if (max_ua < limits->high_start) {
+ /*
+ * Less than high_start, so set the minimal current
+ * (turn Low Bit off, 0 as high bits).
+ */
+ *dst = 0x0;
+ return 0;
+ }
+
+ /* max_ua is in range: <high_start, infinite>, cut it to limits.max */
+ max_ua = min(limits->max, max_ua);
+ max_ua -= limits->high_start;
+ /*
+ * There is no risk of overflow 'max_ua' here because:
+ * - max_ua >= limits.high_start
+ * - BUILD_BUG checks that 'limits' are: max >= high_start + high_step
+ */
+ current_bits = max_ua / limits->high_step;
+
+ /* Turn Low Bit on (use range <limits.high_start, limits.max>) ... */
+ *dst = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
+ /* and set proper High Bits */
+ *dst |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(maxim_charger_calc_reg_current);
+
static const struct mfd_cell max14577_devs[] = {
{
.name = "max14577-muic",
@@ -463,6 +544,20 @@ static int __init max14577_i2c_init(void)
BUILD_BUG_ON(ARRAY_SIZE(max14577_i2c_id) != MAXIM_DEVICE_TYPE_NUM);
BUILD_BUG_ON(ARRAY_SIZE(max14577_dt_match) != MAXIM_DEVICE_TYPE_NUM);
+ /* Valid charger current values must be provided for each chipset */
+ BUILD_BUG_ON(ARRAY_SIZE(maxim_charger_currents) != MAXIM_DEVICE_TYPE_NUM);
+
+ /* Check for valid values for charger */
+ BUILD_BUG_ON(MAX14577_CHARGER_CURRENT_LIMIT_HIGH_START +
+ MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP * 0xf !=
+ MAX14577_CHARGER_CURRENT_LIMIT_MAX);
+ BUILD_BUG_ON(MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP == 0);
+
+ BUILD_BUG_ON(MAX77836_CHARGER_CURRENT_LIMIT_HIGH_START +
+ MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP * 0xf !=
+ MAX77836_CHARGER_CURRENT_LIMIT_MAX);
+ BUILD_BUG_ON(MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP == 0);
+
return i2c_add_driver(&max14577_i2c_driver);
}
subsys_initcall(max14577_i2c_init);
diff --git a/drivers/regulator/max14577.c b/drivers/regulator/max14577.c
index 5d9c605cf534..0ff5a20ac958 100644
--- a/drivers/regulator/max14577.c
+++ b/drivers/regulator/max14577.c
@@ -22,42 +22,6 @@
#include <linux/mfd/max14577-private.h>
#include <linux/regulator/of_regulator.h>
-/*
- * Valid limits of current for max14577 and max77836 chargers.
- * They must correspond to MBCICHWRCL and MBCICHWRCH fields in CHGCTRL4
- * register for given chipset.
- */
-struct maxim_charger_current {
- /* Minimal current, set in CHGCTRL4/MBCICHWRCL, uA */
- unsigned int min;
- /*
- * Minimal current when high setting is active,
- * set in CHGCTRL4/MBCICHWRCH, uA
- */
- unsigned int high_start;
- /* Value of one step in high setting, uA */
- unsigned int high_step;
- /* Maximum current of high setting, uA */
- unsigned int max;
-};
-
-/* Table of valid charger currents for different Maxim chipsets */
-static const struct maxim_charger_current maxim_charger_currents[] = {
- [MAXIM_DEVICE_TYPE_UNKNOWN] = { 0, 0, 0, 0 },
- [MAXIM_DEVICE_TYPE_MAX14577] = {
- .min = MAX14577_REGULATOR_CURRENT_LIMIT_MIN,
- .high_start = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START,
- .high_step = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
- .max = MAX14577_REGULATOR_CURRENT_LIMIT_MAX,
- },
- [MAXIM_DEVICE_TYPE_MAX77836] = {
- .min = MAX77836_REGULATOR_CURRENT_LIMIT_MIN,
- .high_start = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START,
- .high_step = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
- .max = MAX77836_REGULATOR_CURRENT_LIMIT_MAX,
- },
-};
-
static int max14577_reg_is_enabled(struct regulator_dev *rdev)
{
int rid = rdev_get_id(rdev);
@@ -103,8 +67,8 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev)
static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
int min_uA, int max_uA)
{
- int i, current_bits = 0xf;
u8 reg_data;
+ int ret;
struct max14577 *max14577 = rdev_get_drvdata(rdev);
const struct maxim_charger_current *limits =
&maxim_charger_currents[max14577->dev_type];
@@ -112,35 +76,9 @@ static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
if (rdev_get_id(rdev) != MAX14577_CHARGER)
return -EINVAL;
- if (min_uA > limits->max || max_uA < limits->min)
- return -EINVAL;
-
- if (max_uA < limits->high_start) {
- /*
- * Less than high_start,
- * so set the minimal current (turn only Low Bit off)
- */
- u8 reg_data = 0x0 << CHGCTRL4_MBCICHWRCL_SHIFT;
- return max14577_update_reg(rdev->regmap,
- MAX14577_CHG_REG_CHG_CTRL4,
- CHGCTRL4_MBCICHWRCL_MASK, reg_data);
- }
-
- /*
- * max_uA is in range: <high_start, inifinite>, so search for
- * valid current starting from maximum current.
- */
- for (i = limits->max; i >= limits->high_start; i -= limits->high_step) {
- if (i <= max_uA)
- break;
- current_bits--;
- }
- BUG_ON(current_bits < 0); /* Cannot happen */
-
- /* Turn Low Bit on (use range high_start-max)... */
- reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
- /* and set proper High Bits */
- reg_data |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT;
+ ret = maxim_charger_calc_reg_current(limits, min_uA, max_uA, ®_data);
+ if (ret)
+ return ret;
return max14577_update_reg(rdev->regmap, MAX14577_CHG_REG_CHG_CTRL4,
CHGCTRL4_MBCICHWRCL_MASK | CHGCTRL4_MBCICHWRCH_MASK,
@@ -442,16 +380,6 @@ static struct platform_driver max14577_regulator_driver = {
static int __init max14577_regulator_init(void)
{
- /* Check for valid values for charger */
- BUILD_BUG_ON(MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
- MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
- MAX14577_REGULATOR_CURRENT_LIMIT_MAX);
- BUILD_BUG_ON(MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START +
- MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
- MAX77836_REGULATOR_CURRENT_LIMIT_MAX);
- /* Valid charger current values must be provided for each chipset */
- BUILD_BUG_ON(ARRAY_SIZE(maxim_charger_currents) != MAXIM_DEVICE_TYPE_NUM);
-
BUILD_BUG_ON(ARRAY_SIZE(max14577_supported_regulators) != MAX14577_REGULATOR_NUM);
BUILD_BUG_ON(ARRAY_SIZE(max77836_supported_regulators) != MAX77836_REGULATOR_NUM);
diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h
index d6f321699b89..7d514839c764 100644
--- a/include/linux/mfd/max14577-private.h
+++ b/include/linux/mfd/max14577-private.h
@@ -281,17 +281,17 @@ enum max14577_charger_reg {
#define CHGCTRL7_OTPCGHCVS_SHIFT 0
#define CHGCTRL7_OTPCGHCVS_MASK (0x3 << CHGCTRL7_OTPCGHCVS_SHIFT)
-/* MAX14577 regulator current limits (as in CHGCTRL4 register), uA */
-#define MAX14577_REGULATOR_CURRENT_LIMIT_MIN 90000
-#define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START 200000
-#define MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP 50000
-#define MAX14577_REGULATOR_CURRENT_LIMIT_MAX 950000
-
-/* MAX77836 regulator current limits (as in CHGCTRL4 register), uA */
-#define MAX77836_REGULATOR_CURRENT_LIMIT_MIN 45000
-#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START 100000
-#define MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP 25000
-#define MAX77836_REGULATOR_CURRENT_LIMIT_MAX 475000
+/* MAX14577 charger current limits (as in CHGCTRL4 register), uA */
+#define MAX14577_CHARGER_CURRENT_LIMIT_MIN 90000U
+#define MAX14577_CHARGER_CURRENT_LIMIT_HIGH_START 200000U
+#define MAX14577_CHARGER_CURRENT_LIMIT_HIGH_STEP 50000U
+#define MAX14577_CHARGER_CURRENT_LIMIT_MAX 950000U
+
+/* MAX77836 charger current limits (as in CHGCTRL4 register), uA */
+#define MAX77836_CHARGER_CURRENT_LIMIT_MIN 45000U
+#define MAX77836_CHARGER_CURRENT_LIMIT_HIGH_START 100000U
+#define MAX77836_CHARGER_CURRENT_LIMIT_HIGH_STEP 25000U
+#define MAX77836_CHARGER_CURRENT_LIMIT_MAX 475000U
/* MAX14577 regulator SFOUT LDO voltage, fixed, uV */
#define MAX14577_REGULATOR_SAFEOUT_VOLTAGE 4900000
diff --git a/include/linux/mfd/max14577.h b/include/linux/mfd/max14577.h
index c83fbed1c7b6..3c098d57b1d1 100644
--- a/include/linux/mfd/max14577.h
+++ b/include/linux/mfd/max14577.h
@@ -74,4 +74,27 @@ struct max14577_platform_data {
struct max14577_regulator_platform_data *regulators;
};
+/*
+ * Valid limits of current for max14577 and max77836 chargers.
+ * They must correspond to MBCICHWRCL and MBCICHWRCH fields in CHGCTRL4
+ * register for given chipset.
+ */
+struct maxim_charger_current {
+ /* Minimal current, set in CHGCTRL4/MBCICHWRCL, uA */
+ unsigned int min;
+ /*
+ * Minimal current when high setting is active,
+ * set in CHGCTRL4/MBCICHWRCH, uA
+ */
+ unsigned int high_start;
+ /* Value of one step in high setting, uA */
+ unsigned int high_step;
+ /* Maximum current of high setting, uA */
+ unsigned int max;
+};
+
+extern const struct maxim_charger_current maxim_charger_currents[];
+extern int maxim_charger_calc_reg_current(const struct maxim_charger_current *limits,
+ unsigned int min_ua, unsigned int max_ua, u8 *dst);
+
#endif /* __MAX14577_H__ */
--
1.9.1
^ permalink raw reply related
* [PATCH v3 1/6] charger: max14577: Add support for MAX77836 charger
From: Krzysztof Kozlowski @ 2014-07-04 10:12 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov, David Woodhouse, linux-kernel, linux-api
Cc: Samuel Ortiz, Lee Jones, Liam Girdwood, Mark Brown, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Kyungmin Park,
Marek Szyprowski, Bartlomiej Zolnierkiewicz, Krzysztof Kozlowski,
Anton Vorontsov
In-Reply-To: <1404468765-10818-1-git-send-email-k.kozlowski@samsung.com>
Add support for MAX77836 charger to the max14577 driver. The MAX77836
charger is almost the same as 14577 model except:
- No dead-battery detection;
- Support for special charger (like in MAX77693);
- Support for DX over-voltage protection (like in MAX77693);
- Lower values of charging current (two times lower current for
slow/fast charge, much lower EOC current);
- Slightly different values in ChgTyp field of STATUS2 register. On
MAX14577 0x6 is reserved and 0x7 dead battery. On the MAX77836 the
0x6 means special charger and 0x7 is reserved. Regardless of these
differences the driver maps them to one enum max14577_muic_charger_type.
Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Anton Vorontsov <anton@enomsg.org>
Cc: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
drivers/power/Kconfig | 4 +-
drivers/power/max14577_charger.c | 77 +++++++++++++++++++++++++++++-------
include/linux/mfd/max14577-private.h | 54 ++++++++++++++++++-------
3 files changed, 104 insertions(+), 31 deletions(-)
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index ba6975123071..94086a5238c6 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -318,11 +318,11 @@ config CHARGER_MANAGER
with help of suspend_again support.
config CHARGER_MAX14577
- tristate "Maxim MAX14577 MUIC battery charger driver"
+ tristate "Maxim MAX14577/77836 battery charger driver"
depends on MFD_MAX14577
help
Say Y to enable support for the battery charger control sysfs and
- platform data of MAX14577 MUICs.
+ platform data of MAX14577/77836 MUICs.
config CHARGER_MAX8997
tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
diff --git a/drivers/power/max14577_charger.c b/drivers/power/max14577_charger.c
index fad2a75b3604..19c8f42abf24 100644
--- a/drivers/power/max14577_charger.c
+++ b/drivers/power/max14577_charger.c
@@ -1,7 +1,7 @@
/*
- * Battery charger driver for the Maxim 14577
+ * max14577_charger.c - Battery charger driver for the Maxim 14577/77836
*
- * Copyright (C) 2013 Samsung Electronics
+ * Copyright (C) 2013,2014 Samsung Electronics
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -25,10 +25,35 @@ struct max14577_charger {
struct max14577 *max14577;
struct power_supply charger;
- unsigned int charging_state;
- unsigned int battery_state;
+ unsigned int charging_state;
+ unsigned int battery_state;
};
+/*
+ * Helper function for mapping values of STATUS2/CHGTYP register on max14577
+ * and max77836 chipsets to enum maxim_muic_charger_type.
+ */
+static enum max14577_muic_charger_type maxim_get_charger_type(
+ enum maxim_device_type dev_type, u8 val) {
+ switch (val) {
+ case MAX14577_CHARGER_TYPE_NONE:
+ case MAX14577_CHARGER_TYPE_USB:
+ case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
+ case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
+ case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
+ case MAX14577_CHARGER_TYPE_SPECIAL_1A:
+ return val;
+ case MAX14577_CHARGER_TYPE_DEAD_BATTERY:
+ case MAX14577_CHARGER_TYPE_RESERVED:
+ if (dev_type == MAXIM_DEVICE_TYPE_MAX77836)
+ val |= 0x8;
+ return val;
+ default:
+ WARN_ONCE(1, "max14577: Unsupported chgtyp register value 0x%02x", val);
+ return val;
+ }
+}
+
static int max14577_get_charger_state(struct max14577_charger *chg)
{
struct regmap *rmap = chg->max14577->regmap;
@@ -89,19 +114,23 @@ static int max14577_get_online(struct max14577_charger *chg)
{
struct regmap *rmap = chg->max14577->regmap;
u8 reg_data;
+ enum max14577_muic_charger_type chg_type;
max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data);
reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT);
- switch (reg_data) {
+ chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data);
+ switch (chg_type) {
case MAX14577_CHARGER_TYPE_USB:
case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
case MAX14577_CHARGER_TYPE_SPECIAL_1A:
case MAX14577_CHARGER_TYPE_DEAD_BATTERY:
+ case MAX77836_CHARGER_TYPE_SPECIAL_BIAS:
return 1;
case MAX14577_CHARGER_TYPE_NONE:
case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
case MAX14577_CHARGER_TYPE_RESERVED:
+ case MAX77836_CHARGER_TYPE_RESERVED:
default:
return 0;
}
@@ -118,10 +147,12 @@ static int max14577_get_battery_health(struct max14577_charger *chg)
struct regmap *rmap = chg->max14577->regmap;
int state = POWER_SUPPLY_HEALTH_GOOD;
u8 reg_data;
+ enum max14577_muic_charger_type chg_type;
max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, ®_data);
reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT);
- if (reg_data == MAX14577_CHARGER_TYPE_DEAD_BATTERY) {
+ chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data);
+ if (chg_type == MAX14577_CHARGER_TYPE_DEAD_BATTERY) {
state = POWER_SUPPLY_HEALTH_DEAD;
goto state_set;
}
@@ -167,7 +198,7 @@ static void max14577_charger_reg_init(struct max14577_charger *chg)
CDETCTRL1_CHGDETEN_MASK | CDETCTRL1_CHGTYPMAN_MASK,
reg_data);
- /* Battery Fast-Charge Timer, from SM-V700: 6hrs */
+ /* Battery Fast-Charge Timer, set to: 6hrs */
reg_data = 0x3 << CHGCTRL1_TCHW_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL1, reg_data);
@@ -179,19 +210,22 @@ static void max14577_charger_reg_init(struct max14577_charger *chg)
reg_data |= 0x1 << CHGCTRL2_MBCHOSTEN_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL2, reg_data);
- /* Battery-Charger Constant Voltage (CV) Mode, from SM-V700: 4.35V */
+ /* Battery-Charger Constant Voltage (CV) Mode, set to: 4.35V */
reg_data = 0xf << CHGCTRL3_MBCCVWRC_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL3, reg_data);
/*
- * Fast Battery-Charge Current Low, default 200-950mA
- * Fast Battery-Charge Current High, from SM-V700: 450mA
+ * Fast Battery-Charge Current Low,
+ * default 200-950mA (max14577) / 100-475mA (max77836)
+ *
+ * Fast Battery-Charge Current High,
+ * set to 450mA (max14577) / 225mA (max77836)
*/
reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
reg_data |= 0x5 << CHGCTRL4_MBCICHWRCH_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL4, reg_data);
- /* End-of-Charge Current, from SM-V700: 50mA */
+ /* End-of-Charge Current, set to 50mA (max14577) / 7.5mA (max77836) */
reg_data = 0x0 << CHGCTRL5_EOCS_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL5, reg_data);
@@ -199,7 +233,7 @@ static void max14577_charger_reg_init(struct max14577_charger *chg)
reg_data = 0x0 << CHGCTRL6_AUTOSTOP_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL6, reg_data);
- /* Overvoltage-Protection Threshold, from SM-V700: 6.5V */
+ /* Overvoltage-Protection Threshold, set to 6.5V */
reg_data = 0x2 << CHGCTRL7_OTPCGHCVS_SHIFT;
max14577_write_reg(rmap, MAX14577_REG_CHGCTRL7, reg_data);
}
@@ -215,7 +249,11 @@ static enum power_supply_property max14577_charger_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
};
-static const char *model_name = "MAX14577";
+static const char * const model_names[] = {
+ [MAXIM_DEVICE_TYPE_UNKNOWN] = "MAX14577-like",
+ [MAXIM_DEVICE_TYPE_MAX14577] = "MAX14577",
+ [MAXIM_DEVICE_TYPE_MAX77836] = "MAX77836",
+};
static const char *manufacturer = "Maxim Integrated";
static int max14577_charger_get_property(struct power_supply *psy,
@@ -244,7 +282,8 @@ static int max14577_charger_get_property(struct power_supply *psy,
val->intval = max14577_get_online(chg);
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
- val->strval = model_name;
+ BUILD_BUG_ON(ARRAY_SIZE(model_names) != MAXIM_DEVICE_TYPE_NUM);
+ val->strval = model_names[chg->max14577->dev_type];
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = manufacturer;
@@ -296,6 +335,13 @@ static int max14577_charger_remove(struct platform_device *pdev)
return 0;
}
+static const struct platform_device_id max14577_charger_id[] = {
+ { "max14577-charger", MAXIM_DEVICE_TYPE_MAX14577, },
+ { "max77836-charger", MAXIM_DEVICE_TYPE_MAX77836, },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, max14577_regulator_id);
+
static struct platform_driver max14577_charger_driver = {
.driver = {
.owner = THIS_MODULE,
@@ -303,9 +349,10 @@ static struct platform_driver max14577_charger_driver = {
},
.probe = max14577_charger_probe,
.remove = max14577_charger_remove,
+ .id_table = max14577_charger_id,
};
module_platform_driver(max14577_charger_driver);
MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
-MODULE_DESCRIPTION("MAXIM 14577 charger driver");
+MODULE_DESCRIPTION("Maxim 14577/77836 charger driver");
MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h
index 499253604026..d6f321699b89 100644
--- a/include/linux/mfd/max14577-private.h
+++ b/include/linux/mfd/max14577-private.h
@@ -72,15 +72,33 @@ enum max14577_muic_reg {
MAX14577_MUIC_REG_END,
};
+/*
+ * Combined charger types for max14577 and max77836.
+ *
+ * On max14577 three lower bits map to STATUS2/CHGTYP field.
+ * However the max77836 has different two last values of STATUS2/CHGTYP.
+ * To indicate the difference enum has two additional values for max77836.
+ * These values are just a register value bitwise OR with 0x8.
+ */
enum max14577_muic_charger_type {
- MAX14577_CHARGER_TYPE_NONE = 0,
- MAX14577_CHARGER_TYPE_USB,
- MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT,
- MAX14577_CHARGER_TYPE_DEDICATED_CHG,
- MAX14577_CHARGER_TYPE_SPECIAL_500MA,
- MAX14577_CHARGER_TYPE_SPECIAL_1A,
- MAX14577_CHARGER_TYPE_RESERVED,
- MAX14577_CHARGER_TYPE_DEAD_BATTERY = 7,
+ MAX14577_CHARGER_TYPE_NONE = 0x0,
+ MAX14577_CHARGER_TYPE_USB = 0x1,
+ MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT = 0x2,
+ MAX14577_CHARGER_TYPE_DEDICATED_CHG = 0x3,
+ MAX14577_CHARGER_TYPE_SPECIAL_500MA = 0x4,
+ /* Special 1A or 2A charger */
+ MAX14577_CHARGER_TYPE_SPECIAL_1A = 0x5,
+ /* max14577: reserved, used on max77836 */
+ MAX14577_CHARGER_TYPE_RESERVED = 0x6,
+ /* max14577: dead-battery charing with maximum current 100mA */
+ MAX14577_CHARGER_TYPE_DEAD_BATTERY = 0x7,
+ /*
+ * max77836: special charger (bias on D+/D-),
+ * matches register value of 0x6
+ */
+ MAX77836_CHARGER_TYPE_SPECIAL_BIAS = 0xe,
+ /* max77836: reserved, register value 0x7 */
+ MAX77836_CHARGER_TYPE_RESERVED = 0xf,
};
/* MAX14577 interrupts */
@@ -121,13 +139,15 @@ enum max14577_muic_charger_type {
#define STATUS2_CHGTYP_SHIFT 0
#define STATUS2_CHGDETRUN_SHIFT 3
#define STATUS2_DCDTMR_SHIFT 4
-#define STATUS2_DBCHG_SHIFT 5
+#define MAX14577_STATUS2_DBCHG_SHIFT 5
+#define MAX77836_STATUS2_DXOVP_SHIFT 5
#define STATUS2_VBVOLT_SHIFT 6
#define MAX77836_STATUS2_VIDRM_SHIFT 7
#define STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT)
#define STATUS2_CHGDETRUN_MASK BIT(STATUS2_CHGDETRUN_SHIFT)
#define STATUS2_DCDTMR_MASK BIT(STATUS2_DCDTMR_SHIFT)
-#define STATUS2_DBCHG_MASK BIT(STATUS2_DBCHG_SHIFT)
+#define MAX14577_STATUS2_DBCHG_MASK BIT(MAX14577_STATUS2_DBCHG_SHIFT)
+#define MAX77836_STATUS2_DXOVP_MASK BIT(MAX77836_STATUS2_DXOVP_SHIFT)
#define STATUS2_VBVOLT_MASK BIT(STATUS2_VBVOLT_SHIFT)
#define MAX77836_STATUS2_VIDRM_MASK BIT(MAX77836_STATUS2_VIDRM_SHIFT)
@@ -177,9 +197,11 @@ enum max14577_muic_charger_type {
#define CTRL3_JIGSET_SHIFT 0
#define CTRL3_BOOTSET_SHIFT 2
#define CTRL3_ADCDBSET_SHIFT 4
+#define CTRL3_WBTH_SHIFT 6
#define CTRL3_JIGSET_MASK (0x3 << CTRL3_JIGSET_SHIFT)
#define CTRL3_BOOTSET_MASK (0x3 << CTRL3_BOOTSET_SHIFT)
#define CTRL3_ADCDBSET_MASK (0x3 << CTRL3_ADCDBSET_SHIFT)
+#define CTRL3_WBTH_MASK (0x3 << CTRL3_WBTH_SHIFT)
/* Slave addr = 0x4A: Charger */
enum max14577_charger_reg {
@@ -210,16 +232,20 @@ enum max14577_charger_reg {
#define CDETCTRL1_CHGTYPMAN_SHIFT 1
#define CDETCTRL1_DCDEN_SHIFT 2
#define CDETCTRL1_DCD2SCT_SHIFT 3
-#define CDETCTRL1_DCHKTM_SHIFT 4
-#define CDETCTRL1_DBEXIT_SHIFT 5
+#define MAX14577_CDETCTRL1_DCHKTM_SHIFT 4
+#define MAX77836_CDETCTRL1_CDLY_SHIFT 4
+#define MAX14577_CDETCTRL1_DBEXIT_SHIFT 5
+#define MAX77836_CDETCTRL1_DCDCPL_SHIFT 5
#define CDETCTRL1_DBIDLE_SHIFT 6
#define CDETCTRL1_CDPDET_SHIFT 7
#define CDETCTRL1_CHGDETEN_MASK BIT(CDETCTRL1_CHGDETEN_SHIFT)
#define CDETCTRL1_CHGTYPMAN_MASK BIT(CDETCTRL1_CHGTYPMAN_SHIFT)
#define CDETCTRL1_DCDEN_MASK BIT(CDETCTRL1_DCDEN_SHIFT)
#define CDETCTRL1_DCD2SCT_MASK BIT(CDETCTRL1_DCD2SCT_SHIFT)
-#define CDETCTRL1_DCHKTM_MASK BIT(CDETCTRL1_DCHKTM_SHIFT)
-#define CDETCTRL1_DBEXIT_MASK BIT(CDETCTRL1_DBEXIT_SHIFT)
+#define MAX14577_CDETCTRL1_DCHKTM_MASK BIT(MAX14577_CDETCTRL1_DCHKTM_SHIFT)
+#define MAX77836_CDETCTRL1_CDDLY_MASK BIT(MAX77836_CDETCTRL1_CDDLY_SHIFT)
+#define MAX14577_CDETCTRL1_DBEXIT_MASK BIT(MAX14577_CDETCTRL1_DBEXIT_SHIFT)
+#define MAX77836_CDETCTRL1_DCDCPL_MASK BIT(MAX77836_CDETCTRL1_DCDCPL_SHIFT)
#define CDETCTRL1_DBIDLE_MASK BIT(CDETCTRL1_DBIDLE_SHIFT)
#define CDETCTRL1_CDPDET_MASK BIT(CDETCTRL1_CDPDET_SHIFT)
--
1.9.1
^ permalink raw reply related
* [PATCH v3 0/6] charger/mfd: max14577: Add support for MAX77836
From: Krzysztof Kozlowski @ 2014-07-04 10:12 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov, David Woodhouse,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA
Cc: Samuel Ortiz, Lee Jones, Liam Girdwood, Mark Brown, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Kyungmin Park,
Marek Szyprowski, Bartlomiej Zolnierkiewicz, Krzysztof Kozlowski
Hi,
This is third version of patches adding support for MAX77836
device to the max14577 drivers. The first part of patches (main MFD,
extcon and regulator drivers) was merged already.
The patches 1, 2 and 3 depend on each other so they should be
pulled at once. Patches 4, 5 and 6 can be applied independently.
This patchset was already reviewed by some of the maintainers during
previous submissions.
I only need acks from power tree (patches: 1, 3, 4, 6).
Changes since v2
================
1. charger: Use sysfs instead of DTS for setting the fast charge timer.
The charger driver now selects the CONFIG_SYSFS and exports
a DEVICE_ATTR. (suggested by Mark Rutland)
2. Add patch 6 with documentation of exported sysfs entry for fast
charge timer.
3. charger 3/6: Add missing 'break' in switch parsing valid values
for fast charge timer.
Changes since v1
================
1. charger 3/5: Add an error message for each unsuccessful parse of DT
property (suggested by Mark Rutland).
2. charger 3/5: Use 'u32' type for storing values from DT (suggested
by Mark Rutland).
3. charger 3/5: Remove an error message for memory allocation failure.
The patchset (first and second part of the MAX77836 drivers) has been
on the lists since January. Changelog for the first part of drivers:
https://www.mail-archive.com/linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org/msg628696.html
Best regards,
Krzysztof Kozlowski
Krzysztof Kozlowski (6):
charger: max14577: Add support for MAX77836 charger
regulator/mfd: max14577: Export symbols for calculating charger
current
charger: max14577: Configure battery-dependent settings from DTS and
sysfs
power: max17040: Add ID for MAX77836 Fuel Gauge block
devicetree: mfd: max14577: Add device tree bindings document
Documentation: charger: max14577: Document exported sysfs entry
Documentation/ABI/testing/sysfs-class-power | 14 +
Documentation/devicetree/bindings/mfd/max14577.txt | 146 ++++++++
drivers/mfd/max14577.c | 100 +++++-
drivers/power/Kconfig | 5 +-
drivers/power/max14577_charger.c | 370 +++++++++++++++++++--
drivers/power/max17040_battery.c | 1 +
drivers/regulator/max14577.c | 80 +----
include/linux/mfd/max14577-private.h | 95 ++++--
include/linux/mfd/max14577.h | 30 ++
9 files changed, 703 insertions(+), 138 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/max14577.txt
--
1.9.1
^ permalink raw reply
* Re: [PATCH v2 2/4] mm: introduce fincore()
From: Christoph Hellwig @ 2014-07-04 10:12 UTC (permalink / raw)
To: Naoya Horiguchi
Cc: Andrew Morton, Konstantin Khlebnikov, Wu Fengguang,
Arnaldo Carvalho de Melo, Borislav Petkov, Kirill A. Shutemov,
Johannes Weiner, Rusty Russell, David Miller, Andres Freund,
linux-kernel, linux-mm, Dave Hansen, Christoph Hellwig,
Michael Kerrisk, Linux API, Naoya Horiguchi
In-Reply-To: <1404424335-30128-3-git-send-email-n-horiguchi@ah.jp.nec.com>
On Thu, Jul 03, 2014 at 05:52:13PM -0400, Naoya Horiguchi wrote:
> This patch provides a new system call fincore(2), which provides mincore()-
> like information, i.e. page residency of a given file. But unlike mincore(),
> fincore() has a mode flag which allows us to extract detailed information
> about page cache like pfn and page flag. This kind of information is very
> helpful, for example when applications want to know the file cache status
> to control the IO on their own way.
It's still a nasty multiplexer for multiple different reporting formats
in a single system call. How about your really just do a fincore that
mirrors mincore instead of piggybacking exports of various internal
flags (tags and page flags onto it.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [RFC PATCH 00/11] Adding FreeBSD's Capsicum security framework (part 1)
From: Paolo Bonzini @ 2014-07-04 7:03 UTC (permalink / raw)
To: David Drysdale
Cc: LSM List, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Greg Kroah-Hartman, Alexander Viro, Meredydd Luff, Kees Cook,
James Morris, Linux API, qemu-devel
In-Reply-To: <20140703183927.GA1629-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Il 03/07/2014 20:39, David Drysdale ha scritto:
> On Thu, Jul 03, 2014 at 11:12:33AM +0200, Paolo Bonzini wrote:
>> Given Linux's previous experience with BPF filters, what do you
>> think about attaching specific BPF programs to file descriptors?
>> Then whenever a syscall is run that affects a file descriptor, the
>> BPF program for the file descriptor (attached to a struct file* as
>> in Capsicum) would run in addition to the process-wide filter.
>
> That sounds kind of clever, but also kind of complicated.
>
> Off the top of my head, one particular problem is that not all
> fd->struct file conversions in the kernel are completely specified
> by an enclosing syscall and the explicit values of its parameters.
>
> For example, the actual contents of the arguments to io_submit(2)
> aren't visible to a seccomp-bpf program (as it can't read the __user
> memory for the iocb structures), and so it can't distinguish a
> read from a write.
I think that's more easily done by opening the file as O_RDONLY/O_WRONLY
/O_RDWR. You could do it by running the file descriptor's seccomp-bpf
program once per iocb with synthesized syscall numbers and argument
vectors.
BTW, there's one thing I'm not sure I understand (because my knowledge
of VFS is really only cursory). Are the capabilities associated to the
file _descriptor_ (a la F_GETFD/SETFD) or _description_
(F_GETFL/SETFL)?!?
If it is the former, there is some value in read/write capabilities
because you could for example block a child process from reading an
eventfd and simulate the two file descriptors returned by pipe(2). But
if it is the latter, it looks like an important usability problem in
the Capsicum model. (Granted, it's just about usability---in the end
it does exactly what it's meant and documented to do).
> Also, there could potentially be some odd interactions with file
> descriptors passed between processes, if the BPF program relies
> on assumptions about the environment of the original process. For
> example, what happens if an x86_64 process passes a filter-attached
> FD to an ia32 process? Given that the syscall numbers are
> arch-specific, I guess that means the filter program would have
> to include arch-specific branches for any possible variant.
This is the same for using seccompv2 to limit child processes, no? So
there may be a problem but it has to be solved anyway by libseccomp.
> More generally, I suspect that keeping things simpler will end
> up being more secure. Capsicum was based on well-studied ideas
> from the world of object capability-based security, and I'd be
> nervous about adding complications that take us further away from
> that.
True.
> That mapping would also need be kept closely in sync with the kernel
> and other system libraries -- if a new syscall is added and libc (or
> some other library) started using it, the equivalent BPF chunks would
> need to be updated to cope.
Again, this is the same problem that has to be solved for process-wide
seccompv2.
>>> [Capsicum also includes 'capability mode', which locks down the
>>> available syscalls so the rights restrictions can't just be bypassed
>>> by opening new file descriptors; I'll describe that separately later.]
>>
>> This can also be implemented in userspace via seccomp and
>> PR_SET_NO_NEW_PRIVS.
>
> Well, mostly (and in fact I've got an attempt to do exactly that at
> https://github.com/google/capsicum-test/blob/dev/linux-bpf-capmode.c).
>
> [..] there's one awkward syscall case. In capability mode we'd like
> to prevent processes from sending signals with kill(2)/tgkill(2)
> to other processes, but they should still be able to send themselves
> signals. For example, abort(3) generates:
> tgkill(gettid(), gettid(), SIGABRT)
>
> Only allowing kill(self) is hard to encode in a seccomp-bpf program, at
> least in a way that survives forking.
I guess the thread id could be added as a special seccomp-bpf argument
(ancillary datum?).
> Finally, capability mode also turns on strict-relative lookups
> process-wide; in other words, every openat(dfd, ...) operation
> acts as though it has the O_BENEATH_ONLY flag set, regardless of
> whether the dfd is a Capsicum capability. I can't see a way to
> do that with a BPF program (although it would be possible to add
> a filter that polices the requirement to include O_BENEATH_ONLY
> rather than implicitly adding it).
That can be a new prctl (one that PR_SET_NO_NEW_PRIVS would lock up).
It seems useful independent of Capsicum, and the Linux APIs tend to be
fine-grained more often than coarse-grained.
>>> [Policing the rights checks anywhere else, for example at the system
>>> call boundary, isn't a good idea because it opens up the possibility
>>> of time-of-check/time-of-use (TOCTOU) attacks [2] where FDs are
>>> changed (as openat/close/dup2 are allowed in capability mode) between
>>> the 'check' at syscall entry and the 'use' at fget() invocation.]
>>
>> In the case of BPF filters, I wonder if you could stash the BPF
>> "environment" somewhere and then use it at fget() invocation.
>> Alternatively, it can be reconstructed at fget() time, similar to
>> your introduction of fgetr().
>
> Stashing something at syscall entry to be referred to later always
> makes me worry about TOCTOU vulnerabilities, but the details might
> be OK in this case (given that no check occurs at syscall entry)...
Yeah, that was pretty much the idea. But I was cautious enough to
label it with "I wonder"...
Paolo
^ permalink raw reply
* Re: [PATCH v9] mm: support madvise(MADV_FREE)
From: Minchan Kim @ 2014-07-04 6:41 UTC (permalink / raw)
To: Martin Schwidefsky
Cc: Kirill A. Shutemov, Andrew Morton,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-mm-Bw31MaZKKs3YtjvyW6yDsg, Michael Kerrisk, Linux API,
Hugh Dickins, Johannes Weiner, Rik van Riel, KOSAKI Motohiro,
Mel Gorman, Jason Evans, Zhang Yanfei, Heiko Carstens,
linux390-tA70FqPdS9bQT0dZR+AlfA, Gerald Schaefer
In-Reply-To: <20140703180100.5f24a139@mschwide>
Hello,
On Thu, Jul 03, 2014 at 06:01:00PM +0200, Martin Schwidefsky wrote:
> On Thu, 3 Jul 2014 17:37:29 +0900
> Minchan Kim <minchan-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
>
> > Hello,
> >
> > On Thu, Jul 03, 2014 at 10:29:01AM +0200, Martin Schwidefsky wrote:
> > > On Thu, 3 Jul 2014 16:29:54 +0900
> > > Minchan Kim <minchan-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> > >
> > > > Hello,
> > > >
> > > > On Thu, Jul 03, 2014 at 10:03:19AM +0900, Minchan Kim wrote:
> > > > > Hello,
> > > > >
> > > > > On Tue, Jul 01, 2014 at 05:50:58PM +0300, Kirill A. Shutemov wrote:
> > > > > > On Tue, Jul 01, 2014 at 09:36:15AM +0900, Minchan Kim wrote:
> > > > > > > + do {
> > > > > > > + /*
> > > > > > > + * XXX: We can optimize with supporting Hugepage free
> > > > > > > + * if the range covers.
> > > > > > > + */
> > > > > > > + next = pmd_addr_end(addr, end);
> > > > > > > + if (pmd_trans_huge(*pmd))
> > > > > > > + split_huge_page_pmd(vma, addr, pmd);
> > > > > >
> > > > > > Could you implement proper THP support before upstreaming the feature?
> > > > > > It shouldn't be a big deal.
> > > > >
> > > > > Okay, Hope to review.
> > > > >
> > > > > Thanks for the feedback!
> > > > >
> > > >
> > > > I tried to implement it but had a issue.
> > > >
> > > > I need pmd_mkold, pmd_mkclean for MADV_FREE operation and pmd_dirty for
> > > > page_referenced. When I investigate all of arches supported THP,
> > > > it's not a big deal but s390 is not sure to me who has no idea of
> > > > soft tracking of s390 by storage key instead of page table information.
> > > > Cced s390 maintainer. Hope to help.
> > >
> > > Storage key for dirty and referenced tracking is a thing of the past.
> > > The current code for s390 uses software tracking for dirty and referenced.
> > > There is one catch though, for ptes the software implementation covers
> > > dirty and referenced bit but for pmds only referenced bit is available.
> > > The reason is that there is no free bit left in the pmd entry for the
> > > software dirty bit.
> >
> > Thanks for the quick reply.
> >
> > >
> > > > So, if there isn't any help from s390, I should introduce
> > > > HAVE_ARCH_THP_MADVFREE to disable MADV_FREE support of THP in s390 but
> > > > not want to introduce such new config.
> > >
> > > Why is the dirty bit for pmds needed for the MADV_FREE implementation?
> >
> > MADV_FREE semantic want it.
> >
> > When madvise syscall is called, VM clears dirty bit of ptes of
> > the range. If memory pressure happens, VM checks dirty bit of
> > page table and if it found still "clean", it means it's a
> > "lazyfree pages" so VM could discard the page instead of swapping out.
> > Once there was store operation for the page before VM peek a page
> > to reclaim, dirty bit is set so VM can swap out the page instead of
> > discarding to keep up-to-date contents.
> >
> > If it's hard on s390, maybe we could use just reference bit
> > instead of dirty bit to check recent access but it might change
> > semantic a bit with other OSes. :(
>
> Just discussed this with Gerald and we found a trick how we can add
> a dirty bit to the pmd entries. That will be a non-trivial patch but
> we can do it. Until that time you could just define pmd_dirty to
> always return true and the code should "work" in the sense that it
> does not break anything.
Will work.
I will post a patch when I finish the work.
Hope to review.
Thanks for your advise!
>
> --
> blue skies,
> Martin.
>
> "Reality continues to ruin my life." - Calvin.
>
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo-Bw31MaZKKs0EbZ0PF+XxCw@public.gmane.org For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Don't email: <a href=mailto:"dont-Bw31MaZKKs3YtjvyW6yDsg@public.gmane.org"> email-Bw31MaZKKs3YtjvyW6yDsg@public.gmane.org </a>
--
Kind regards,
Minchan Kim
^ permalink raw reply
* Re: [PATCH v2 1/4] define PAGECACHE_TAG_* as enumeration under include/uapi
From: Naoya Horiguchi @ 2014-07-04 1:41 UTC (permalink / raw)
To: Dave Chinner
Cc: Andrew Morton, Konstantin Khlebnikov, Wu Fengguang,
Arnaldo Carvalho de Melo, Borislav Petkov, Kirill A. Shutemov,
Johannes Weiner, Rusty Russell, David Miller, Andres Freund,
linux-kernel, linux-mm, Dave Hansen, Christoph Hellwig,
Michael Kerrisk, Linux API, Naoya Horiguchi
In-Reply-To: <20140704011639.GG9508@dastard>
On Fri, Jul 04, 2014 at 11:16:39AM +1000, Dave Chinner wrote:
> On Thu, Jul 03, 2014 at 05:52:12PM -0400, Naoya Horiguchi wrote:
> > We need the pagecache tags to be exported to userspace later in this
> > series for fincore(2), so this patch moves the definition to the new
> > include file for preparation. We also use the number of pagecache tags,
> > so this patch also adds it.
> >
> > Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
>
> NACK.
>
> The radix tree tags are deeply internal implementation details.
> They are an artifact of the current mark-and-sweep writeback
> algorithm, and as such should never, ever be exposed to userspace,
> let alone fixed in an ABI we need to support forever more.
Hm, OK, so I'll do whole this series without pagecache tag things.
Thanks,
Naoya Horiguchi
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH v2 1/4] define PAGECACHE_TAG_* as enumeration under include/uapi
From: Dave Chinner @ 2014-07-04 1:16 UTC (permalink / raw)
To: Naoya Horiguchi
Cc: Andrew Morton, Konstantin Khlebnikov, Wu Fengguang,
Arnaldo Carvalho de Melo, Borislav Petkov, Kirill A. Shutemov,
Johannes Weiner, Rusty Russell, David Miller, Andres Freund,
linux-kernel, linux-mm, Dave Hansen, Christoph Hellwig,
Michael Kerrisk, Linux API, Naoya Horiguchi
In-Reply-To: <1404424335-30128-2-git-send-email-n-horiguchi@ah.jp.nec.com>
On Thu, Jul 03, 2014 at 05:52:12PM -0400, Naoya Horiguchi wrote:
> We need the pagecache tags to be exported to userspace later in this
> series for fincore(2), so this patch moves the definition to the new
> include file for preparation. We also use the number of pagecache tags,
> so this patch also adds it.
>
> Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
NACK.
The radix tree tags are deeply internal implementation details.
They are an artifact of the current mark-and-sweep writeback
algorithm, and as such should never, ever be exposed to userspace,
let alone fixed in an ABI we need to support forever more.
Cheers,
Dave.
--
Dave Chinner
david@fromorbit.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* [PATCH v2 4/4] man2/fincore.2: document general description about fincore(2)
From: Naoya Horiguchi @ 2014-07-03 21:52 UTC (permalink / raw)
To: Andrew Morton
Cc: Konstantin Khlebnikov, Wu Fengguang, Arnaldo Carvalho de Melo,
Borislav Petkov, Kirill A. Shutemov, Johannes Weiner,
Rusty Russell, David Miller, Andres Freund, linux-kernel,
linux-mm, Dave Hansen, Christoph Hellwig, Michael Kerrisk,
Linux API, Naoya Horiguchi
In-Reply-To: <1404424335-30128-1-git-send-email-n-horiguchi@ah.jp.nec.com>
This patch adds the man page for the new system call fincore(2).
Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
---
man2/fincore.2 | 383 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 383 insertions(+)
create mode 100644 man2/fincore.2
diff --git v3.16-rc3.orig/man2/fincore.2 v3.16-rc3/man2/fincore.2
new file mode 100644
index 000000000000..dcc596db4fa0
--- /dev/null
+++ v3.16-rc3/man2/fincore.2
@@ -0,0 +1,383 @@
+.\" Copyright (C) 2014 Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
+.\"
+.\" %%%LICENSE_START(VERBATIM)
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one.
+.\"
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date. The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein. The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\"
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\" %%%LICENSE_END
+.\"
+.TH FINCORE 2 2014-07-03 "Linux" "Linux Programmer's Manual"
+.SH NAME
+fincore \- get page cache information
+.SH SYNOPSIS
+.nf
+.B #include <linux/pagecache.h>
+.B #include <linux/kernel-page-flags.h>
+.sp
+.BI "int fincore(int " fd ", loff_t " start ", long " nr_pages ", int " mode ,
+.BI " unsigned char *" vec ", struct fincore_extra *" extra );
+.fi
+.SH DESCRIPTION
+.BR fincore ()
+extracts information of in-core data of (i.e., page caches for)
+the file referred to by the file descriptor
+.IR fd .
+The kernel scans over the page cache tree,
+starting at the in-file offset
+.I start
+(in bytes) until
+.I nr_pages
+entries in the userspace buffer pointed to by
+.IR vec
+are filled with the page cache's data,
+or until the scan reached the end of the file.
+The format of each entry stored in
+.I vec
+depends on the
+.IR mode .
+The extra argument
+.I extra
+is used to pass the additional data between the kernel and the userspace.
+This is optional, so you may set
+.I extra
+to NULL if unnecessary.
+The structure
+.I fincore_extra
+is defined like:
+.in +4n
+.nf
+
+struct fincore_extra {
+ unsigned long nr_entries;
+ unsigned long tags;
+};
+
+.fi
+.in
+The field
+.I nr_entries
+is an output parameter, set to the number of valid entries stored in
+.IR vec
+by the kernel on return.
+The field
+.I tags
+is used as an input and output parameter, indicating the set of
+page cache tags of the caller's interest.
+For more detail,
+see the description about the FINCORE_PAGECACHE_TAG mode below.
+
+The
+.I start
+argument must be aligned to the page cache size boundary.
+In most cases, it's the page size boundary,
+but if called for a hugetlbfs file,
+the page cache size is the size of the hugepage associated with the file,
+so
+.I start
+must be aligned to the hugepage size boundary.
+
+The
+.I mode
+argument determines the data format of each entry in the user buffer
+.IR vec :
+.TP
+.B FINCORE_BMAP (0)
+In this mode,
+1 byte vector is stored in
+.I vec
+on return.
+The least significant bit of each byte is set if the corresponding page
+is currently resident in memory, and is cleared otherwise.
+(The other bits in each byte are undefined and reserved for future use.)
+.LP
+Any of the following flags are to be set to add an 8 byte field in each entry.
+You can set any of these flags at the same time, although you can't set
+FINCORE_BMAP combined with these 8 byte field flags.
+.TP
+.B FINCORE_PGOFF (1)
+This flag indicates that each entry contains a page offset field.
+With this information, you don't have to get data for hole range,
+so they are not stored in
+.I vec
+any longer.
+Note that if you call with this flag, you can't predict how many valid
+entries are stored in the buffer on return. So the
+.I nr_entries
+field in
+.I struct fincore_extra
+is useful if you want it.
+.TP
+.B FINCORE_PFN (2)
+This flag indicates that each entry contains a page frame number
+(i.e., physical address in page size unit) field.
+.TP
+.B FINCORE_PAGE_FLAGS (3)
+This flag indicates that each entry contains a page flags field.
+See KERNEL PAGE FLAGS section for more detail about each bit.
+.TP
+.B FINCORE_PAGECACHE_TAGS (4)
+This flag indicates that each entry contains a page cache tag field.
+See PAGE CACHE TAGS section for more detail about each bit.
+Note that if you set this flag, you must set the argument
+.I extra
+and set
+.I tags
+to the set of page cache tags you are interested in.
+And on return,
+.I tags
+are set by the kernel to the set of tags which is actually scanned.
+.LP
+The size of the buffer
+.I vec
+must be at least
+.I nr_pages
+bytes if FINCORE_BMAP is set,
+and
+.I (8*n*nr_pages)
+bytes if some of the 8 byte field flags are set,
+where
+.I n
+means the number of 8 byte field flags being set.
+When multiple 8 byte field flags are set, the order of data in each
+entry is the same as one in the bit definition order (shown above
+as the numbers in parentheses.)
+For example, when you set FINCORE_PGOFF (bit 1) and FINCORE_PAGE_FLAGS (bit 3,)
+the first 8 bytes in an entry is the page offset,
+and the second 8 bytes is the page flags.
+
+Note that the information returned by the kernel is just a snapshot:
+pages which are not locked in memory can be freed at any moment, and
+the contents of
+.I vec
+may already be stale by the time the caller refers to the data.
+.SH KERNEL PAGE FLAGS
+.TP
+.B KPF_LOCKED (0)
+The lock on the page is held, suggesting that the kernel may be
+doing some page-related sensitive operation.
+.TP
+.B KPF_ERROR (1)
+The page was affected by IO error or memory error, so the data on the page
+might be lost.
+.TP
+.B KPF_REFERENCED (2)
+This page flag is used to control the page reclaim, combined with KPF_ACTIVE.
+.TP
+.B KPF_UPTODATE (3)
+The page has valid contents.
+.TP
+.B KPF_DIRTY (4)
+The data of the page is not synchronized with one on the backing storage.
+.TP
+.B KPF_LRU (5)
+The page is linked to one of the LRU (Least Recently Update) lists.
+.TP
+.B KPF_ACTIVE (6)
+The page is linked to one of the active LRU lists.
+.TP
+.B KPF_SLAB (7)
+The page is used to construct slabs, which is managed by the kernel
+to allocate various types of kernel objects.
+.TP
+.B KPF_WRITEBACK (8)
+The page is under the writeback operation.
+.TP
+.B KPF_RECLAIM (9)
+The page is under the page reclaim operation.
+.TP
+.B KPF_BUDDY (10)
+The page is under the buddy allocator as a free page. Note that this flag
+is only set to the first page of the "buddy" (i.e., the chunk of free pages.)
+.TP
+.B KPF_MMAP (11)
+The page is mapped to the virtual address space of some processes.
+.TP
+.B KPF_ANON (12)
+The page is anonymous page.
+.TP
+.B KPF_SWAPCACHE (13)
+The page has its own copy of the data on the swap device.
+.TP
+.B KPF_SWAPBACKED (14)
+The page can be swapped out. This flag is set on anonymous pages,
+tmpfs pages, or shmem page.
+.TP
+.B KPF_COMPOUND_HEAD (15)
+The page belongs to a high-order page, and is its first page.
+.TP
+.B KPF_COMPOUND_TAIL (16)
+The page belongs to a high-order page, and is not its first page.
+.TP
+.B KPF_HUGE (17)
+The page is used to construct a hugepage.
+.TP
+.B KPF_UNEVICTABLE (18)
+The page is prevented from being freed.
+This is caused by
+.BR mlock (2)
+or shared memory with
+.BR SHM_LOCK .
+.TP
+.B KPF_HWPOISON (19)
+The page is affected by a hardware error on the memory.
+.TP
+.B KPF_NOPAGE (20)
+This is a pseudo page flag which indicates that the given address
+has no struct page backed.
+.TP
+.B KPF_KSM (21)
+The page is a shared page governed by KSM (Kernel Shared Merging.)
+.TP
+.B KPF_THP (22)
+The page is used to construct a transparent hugepage.
+.LP
+.SH PAGE CACHE TAGS
+.TP
+.B PAGECACHE_TAG_DIRTY
+The page is dirty.
+.TP
+.B PAGECACHE_TAG_WRITEBACK
+The page is under the writeback operation.
+.TP
+.B PAGECACHE_TAG_TOWRITE
+The writeback operation on the page will start soon.
+.LP
+.SH RETURN VALUE
+On success,
+.BR fincore ()
+returns 0.
+On error, \-1 is returned, and
+.I errno
+is set appropriately.
+.SH ERRORS
+.TP
+.B EBADF
+.I fd
+is not a valid file descriptor.
+.TP
+.B EFAULT
+.I vec
+points to an invalid address.
+.TP
+.B EINVAL
+.I start
+is unaligned to page cache size or is out-of-range
+(negative or larger than the file size.)
+Or
+.I nr_pages
+is not a positive value.
+Or
+.I mode
+contained a undefined flag, or contained no flag,
+or contained both of FINCORE_BMAP and one of the "8 byte field" flags.
+Or
+.I fincore_extra
+is not given if
+.I FINCORE_PAGECACHE_TAG
+flag is set.
+.SH VERSIONS
+TBD
+.SH CONFORMING TO
+TBD
+
+.SH EXAMPLE
+.PP
+The following program is an example that shows the page cache information
+of the file specified in its first command-line argument to the standard
+output.
+
+.nf
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <uapi/linux/pagecache.h>
+
+#define err(msg) do { perror(msg); exit(1); } while (0)
+
+int main(int argc, char *argv[])
+{
+ int i, j;
+ int fd;
+ int ret;
+ long ps = sysconf(_SC_PAGESIZE);
+ long nr_pages;
+ unsigned char *buf;
+ struct stat stat;
+ struct fincore_extra fe = {};
+
+ fd = open(argv[1], O_RDWR);
+ if (fd == \-1)
+ err("open");
+
+ ret = fstat(fd, &stat);
+ if (ret == \-1)
+ err("fstat");
+ nr_pages = ((stat.st_size + ps \- 1) & (~(ps \- 1))) / ps;
+
+ buf = malloc(nr_pages * 24);
+ if (!buf)
+ err("malloc");
+
+ /* byte map */
+ ret = fincore(fd, 0, nr_pages, FINCORE_BMAP, buf, NULL);
+ if (ret < 0)
+ err("fincore");
+ printf("Page residency:");
+ for (i = 0; i < nr_pages; i++)
+ printf("%d", buf[i]);
+ printf("\\n\\n");
+
+ /* 8 byte entry */
+ ret = fincore(fd, 0, nr_pages,
+ FINCORE_PFN|FINCORE_PAGE_FLAGS, buf, &fe);
+ if (ret < 0)
+ err("fincore");
+ printf("pfn\\tflags %lx\\n", fe.nr_entries);
+ for (i = 0; i < fe.nr_entries; i++) {
+ for (j = 0; j < 2; j++)
+ printf("0x%lx\\t", *(unsigned long *)(buf + (i*2+j)*8));
+ printf("\\n");
+ }
+ printf("\\n");
+
+ /* 8 byte entry with page offset (no hole scanned) */
+ ret = fincore(fd, 0, nr_pages,
+ FINCORE_PGOFF|FINCORE_PFN|FINCORE_PAGE_FLAGS, buf, &fe);
+ if (ret < 0)
+ err("fincore");
+ printf("pgoff\\tpfn\\tflags %lx\\n", fe.nr_entries);
+ for (i = 0; i < fe.nr_entries; i++) {
+ for (j = 0; j < 3; j++)
+ printf("0x%lx\\t", *(unsigned long *)(buf + (i*3+j)*8));
+ printf("\\n");
+ }
+
+ free(buf);
+
+ ret = close(fd);
+ if (ret < 0)
+ err("close");
+ return 0;
+}
+.fi
+.SH SEE ALSO
+.BR mincore (2),
+.BR fsync (2)
--
1.9.3
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* [PATCH v2 3/4] selftests/fincore: add test code for fincore()
From: Naoya Horiguchi @ 2014-07-03 21:52 UTC (permalink / raw)
To: Andrew Morton
Cc: Konstantin Khlebnikov, Wu Fengguang, Arnaldo Carvalho de Melo,
Borislav Petkov, Kirill A. Shutemov, Johannes Weiner,
Rusty Russell, David Miller, Andres Freund, linux-kernel,
linux-mm, Dave Hansen, Christoph Hellwig, Michael Kerrisk,
Linux API, Naoya Horiguchi
In-Reply-To: <1404424335-30128-1-git-send-email-n-horiguchi@ah.jp.nec.com>
This patch adds simple test programs for fincore(), which contains the
following testcase:
- test_smallfile_bytemap
- test_smallfile_pfn
- test_smallfile_multientry
- test_smallfile_pfn_skiphole
- test_smallfile_pagecache_tag
- test_largefile_pfn
- test_largefile_pfn_offset
- test_largefile_pfn_overrun
- test_largefile_pfn_skiphole
- test_tmpfs_pfn
- test_hugetlb_pfn
- test_invalid_start_address
- test_invalid_len
- test_invalid_mode
- test_unaligned_start_address_hugetlb
ChangeLog v2:
- include uapi/linux/pagecache.h
- add testcase test_invalid_start_address and test_invalid_len
- other small changes to adjust for the kernel's changes
Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
---
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/fincore/Makefile | 31 ++
.../selftests/fincore/create_hugetlbfs_file.c | 49 +++
tools/testing/selftests/fincore/fincore.c | 166 +++++++++
tools/testing/selftests/fincore/run_fincoretests | 401 +++++++++++++++++++++
5 files changed, 648 insertions(+)
create mode 100644 tools/testing/selftests/fincore/Makefile
create mode 100644 tools/testing/selftests/fincore/create_hugetlbfs_file.c
create mode 100644 tools/testing/selftests/fincore/fincore.c
create mode 100644 tools/testing/selftests/fincore/run_fincoretests
diff --git v3.16-rc3.orig/tools/testing/selftests/Makefile v3.16-rc3/tools/testing/selftests/Makefile
index e66e710cc595..91e817b87a9e 100644
--- v3.16-rc3.orig/tools/testing/selftests/Makefile
+++ v3.16-rc3/tools/testing/selftests/Makefile
@@ -11,6 +11,7 @@ TARGETS += vm
TARGETS += powerpc
TARGETS += user
TARGETS += sysctl
+TARGETS += fincore
all:
for TARGET in $(TARGETS); do \
diff --git v3.16-rc3.orig/tools/testing/selftests/fincore/Makefile v3.16-rc3/tools/testing/selftests/fincore/Makefile
new file mode 100644
index 000000000000..ab4361c70da5
--- /dev/null
+++ v3.16-rc3/tools/testing/selftests/fincore/Makefile
@@ -0,0 +1,31 @@
+# Makefile for vm selftests
+
+uname_M := $(shell uname -m 2>/dev/null || echo not)
+ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/)
+ifeq ($(ARCH),i386)
+ ARCH := X86
+ CFLAGS := -DCONFIG_X86_32 -D__i386__
+endif
+ifeq ($(ARCH),x86_64)
+ ARCH := X86
+ CFLAGS := -DCONFIG_X86_64 -D__x86_64__
+endif
+
+CC = $(CROSS_COMPILE)gcc
+CFLAGS = -Wall
+CFLAGS += -I../../../../arch/x86/include/generated/
+CFLAGS += -I../../../../include/
+CFLAGS += -I../../../../usr/include/
+CFLAGS += -I../../../../arch/x86/include/
+
+BINARIES = fincore create_hugetlbfs_file
+
+all: $(BINARIES)
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $^
+
+run_tests: all
+ @/bin/sh ./run_fincoretests || (echo "fincoretests: [FAIL]"; exit 1)
+
+clean:
+ $(RM) $(BINARIES)
diff --git v3.16-rc3.orig/tools/testing/selftests/fincore/create_hugetlbfs_file.c v3.16-rc3/tools/testing/selftests/fincore/create_hugetlbfs_file.c
new file mode 100644
index 000000000000..a46ccf0af5f2
--- /dev/null
+++ v3.16-rc3/tools/testing/selftests/fincore/create_hugetlbfs_file.c
@@ -0,0 +1,49 @@
+#define _GNU_SOURCE 1
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#define err(x) (perror(x), exit(1))
+
+unsigned long default_hugepage_size(void)
+{
+ unsigned long hps = 0;
+ char *line = NULL;
+ size_t linelen = 0;
+ FILE *f = fopen("/proc/meminfo", "r");
+ if (!f)
+ err("open /proc/meminfo");
+ while (getline(&line, &linelen, f) > 0) {
+ if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) {
+ hps <<= 10;
+ break;
+ }
+ }
+ free(line);
+ return hps;
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ int fd;
+ char *p;
+ unsigned long hpsize = default_hugepage_size();
+ fd = open(argv[1], O_RDWR|O_CREAT);
+ if (fd == -1)
+ err("open");
+ p = mmap(NULL, 10 * hpsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (p == (void *)-1)
+ err("mmap");
+ memset(p, 'a', 3 * hpsize);
+ memset(p + 7 * hpsize, 'a', 3 * hpsize - 1);
+ ret = close(fd);
+ if (ret == -1)
+ err("close");
+ return 0;
+}
diff --git v3.16-rc3.orig/tools/testing/selftests/fincore/fincore.c v3.16-rc3/tools/testing/selftests/fincore/fincore.c
new file mode 100644
index 000000000000..5722622a3b75
--- /dev/null
+++ v3.16-rc3/tools/testing/selftests/fincore/fincore.c
@@ -0,0 +1,166 @@
+/*
+ * fincore(2) test program
+ */
+
+#define _GNU_SOURCE 1
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <uapi/linux/pagecache.h>
+
+#define err(x) (perror(x), exit(1))
+
+void usage(char *str)
+{
+ printf(
+ "Usage: %s [-s start] [-l len] [-m mode] [-p pagesize] file\n"
+ " -s: start offset (in bytes)\n"
+ " -l: length to scan (in bytes)\n"
+ " -m: fincore mode\n"
+ " -p: set page size (for hugepage)\n"
+ " -h: show this message\n"
+ , str);
+ exit(EXIT_SUCCESS);
+}
+
+static void show_fincore_buffer(long start, long nr_pages, int records_per_page,
+ int mode, unsigned char *buf)
+{
+ int i, j;
+ unsigned char *curuc = (unsigned char *)buf;
+ unsigned long *curul = (unsigned long *)buf;
+
+ for (i = 0; i < nr_pages; i++) {
+ j = 0;
+ if (mode & FINCORE_BMAP)
+ printf("buffer: 0x%lx\t%d", start + i, curuc[i + j]);
+ else if (mode & (FINCORE_LONGENTRY_MASK)) {
+ if (mode & FINCORE_PGOFF)
+ printf("buffer: 0x%lx",
+ curul[i * records_per_page + (j++)]);
+ else
+ printf("buffer: 0x%lx", start + i);
+ if (mode & FINCORE_PFN)
+ printf("\t0x%lx",
+ curul[i * records_per_page + (j++)]);
+ if (mode & FINCORE_PAGE_FLAGS)
+ printf("\t0x%lx",
+ curul[i * records_per_page + (j++)]);
+ if (mode & FINCORE_PAGECACHE_TAGS)
+ printf("\t0x%lx",
+ curul[i * records_per_page + (j++)]);
+ }
+ printf("\n");
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ char c;
+ int fd;
+ int ret;
+ int mode = FINCORE_PFN;
+ int width = sizeof(unsigned char);
+ int records_per_page = 1;
+ long pagesize = sysconf(_SC_PAGESIZE);
+ long nr_pages;
+ unsigned long start = 0;
+ int len_not_given = 1;
+ long len = 0;
+ long buffer_size = 0;
+ unsigned char *buf;
+ struct stat stat;
+ int extra = 0;
+ struct fincore_extra fe = {
+ .tags = PAGECACHE_TAG_DIRTY|PAGECACHE_TAG_WRITEBACK|
+ PAGECACHE_TAG_TOWRITE,
+ };
+
+ while ((c = getopt(argc, argv, "s:l:m:p:et:b:h")) != -1) {
+ switch (c) {
+ case 's':
+ start = strtoul(optarg, NULL, 0);
+ break;
+ case 'l':
+ len_not_given = 0;
+ len = strtol(optarg, NULL, 0);
+ break;
+ case 'm':
+ mode = strtoul(optarg, NULL, 0);
+ break;
+ case 'p':
+ pagesize = strtoul(optarg, NULL, 0);
+ break;
+ case 'e':
+ extra = 1;
+ break;
+ case 't':
+ fe.tags = strtoul(optarg, NULL, 0);
+ break;
+ case 'b':
+ buffer_size = strtoul(optarg, NULL, 0);
+ break;
+ case 'h':
+ default:
+ usage(argv[0]);
+ }
+ }
+
+ fd = open(argv[optind], O_RDWR);
+ if (fd == -1)
+ err("open failed.");
+
+ /* scan to the end of file by default */
+ if (len_not_given) {
+ ret = fstat(fd, &stat);
+ if (ret == -1)
+ err("fstat failed.");
+ len = stat.st_size - start;
+ }
+
+ if (mode & FINCORE_LONGENTRY_MASK) {
+ records_per_page = ((mode & FINCORE_PGOFF ? 1 : 0) +
+ (mode & FINCORE_PFN ? 1 : 0) +
+ (mode & FINCORE_PAGE_FLAGS ? 1 : 0) +
+ (mode & FINCORE_PAGECACHE_TAGS ? 1 : 0)
+ );
+ width = records_per_page * sizeof(unsigned long);
+ }
+
+ nr_pages = ((len + pagesize - 1) & (~(pagesize - 1))) / pagesize;
+ printf("start:0x%lx, len:%ld, mode:%d, pagesize:0x%lx, "
+ "tags:0x%lx,\n buffer_size:0x%lx, nr_pages:0x%lx, width:%d\n",
+ start, len, mode, pagesize, fe.tags,
+ buffer_size, nr_pages, width);
+ buf = malloc(buffer_size > 0 ? buffer_size : nr_pages * width);
+ if (!buf)
+ err("malloc");
+
+ ret = syscall(__NR_fincore, fd, start, nr_pages, mode, buf,
+ extra ? &fe : NULL);
+ if (ret < 0)
+ err("fincore");
+ /*
+ * print buffer to stdout, and parse it later for validation check.
+ * fincore() returns the number of entries written to the buffer.
+ */
+ show_fincore_buffer(start / pagesize, nr_pages, records_per_page,
+ mode, buf);
+
+ if (extra) {
+ printf("fincore_extra->nr_entries: %ld\n", fe.nr_entries);
+ printf("fincore_extra->tags: 0x%lx\n", fe.tags);
+ }
+
+ ret = close(fd);
+ if (ret < 0)
+ err("close");
+ return 0;
+}
diff --git v3.16-rc3.orig/tools/testing/selftests/fincore/run_fincoretests v3.16-rc3/tools/testing/selftests/fincore/run_fincoretests
new file mode 100644
index 000000000000..99c89f915b30
--- /dev/null
+++ v3.16-rc3/tools/testing/selftests/fincore/run_fincoretests
@@ -0,0 +1,401 @@
+#!/bin/bash
+
+WDIR=./fincore_work
+mkdir $WDIR 2> /dev/null
+TMPF=`mktemp --tmpdir=$WDIR -d`
+export LANG=C
+
+sysctl -q vm.nr_hugepages=50
+
+#
+# common routines
+#
+abort() {
+ echo "Test abort"
+ exit 1
+}
+
+create_small_file() {
+ dd if=/dev/urandom of=$WDIR/smallfile bs=4096 count=4 > /dev/null 2>&1
+ dd if=/dev/urandom of=$WDIR/smallfile bs=4096 count=4 seek=8> /dev/null 2>&1
+ date >> $WDIR/smallfile
+ sync
+}
+
+create_large_file() {
+ dd if=/dev/urandom of=$WDIR/largefile bs=4096 count=384 > /dev/null 2>&1
+ dd if=/dev/urandom of=$WDIR/largefile bs=4096 count=384 seek=640> /dev/null 2>&1
+ sync
+}
+
+create_tmpfs_file() {
+ dd if=/dev/urandom of=/tmp/tmpfile bs=4096 count=4 > /dev/null 2>&1
+ dd if=/dev/urandom of=/tmp/tmpfile bs=4096 count=4 seek=8> /dev/null 2>&1
+ date >> /tmp/tmpfile
+ sync
+}
+
+create_hugetlb_file() {
+ if mount | grep $WDIR/hugepages > /dev/null ; then
+ echo "$WDIR/hugepages already mounted"
+ else
+ mkdir -p $WDIR/hugepages 2> /dev/null
+ mount -t hugetlbfs none $WDIR/hugepages 2> /dev/null
+ if [ $? -ne 0 ] ; then
+ echo "Failed to mount hugetlbfs" >&2
+ return 1
+ fi
+ fi
+ local hptotal=$(grep HugePages_Total: /proc/meminfo | tr -s ' ' | cut -f2 -d' ')
+ if [ "$hptotal" -lt 10 ] ; then
+ echo "Hugepage pool size need to be >= 10" >&2
+ return 1
+ fi
+ ./create_hugetlbfs_file $WDIR/hugepages/file
+ if [ $? -ne 0 ] ; then
+ echo "Failed to create hugetlb file" >&2
+ return 1
+ fi
+ return 0;
+}
+
+get_buffer() {
+ cat "$1" | grep '^buffer:' | cut -f 2- -d ' '
+}
+
+get_fincore_extra_nr_entries() {
+ cat "$1" | grep '^fincore_extra->nr_entries' | cut -f 2 -d ' '
+}
+
+get_fincore_extra_tags() {
+ cat "$1" | grep '^fincore_extra->tags' | cut -f 2 -d ' '
+}
+
+nr_of_exist_should_be() {
+ if [ "$1" -ne "$2" ] ; then
+ echo "[FAIL] $3: Number of on-memory pages should be $1, but got $2"
+ return 1
+ fi
+ return 0
+}
+
+nr_of_nonexist_should_be() {
+ if [ "$1" -ne "$2" ] ; then
+ echo "[FAIL] $3: Number of hole entries should be $1, but got $2"
+ return 1
+ fi
+ return 0
+}
+
+nr_of_valid_entries_should_be() {
+ if [ "$1" -ne "$2" ] ; then
+ echo "[FAIL] $3: Number of valid entries should be $1, but got $2"
+ return 1
+ fi
+ return 0
+}
+
+check_einval() {
+ grep "fincore: Invalid argument" "$1" > /dev/null
+}
+
+#
+# Testcases
+#
+test_smallfile_bytemap() {
+ local exist
+ local nonexist
+ create_small_file
+
+ ./fincore -m 0x1 $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ exist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep 1 | wc -l)
+ nonexist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep 0 | wc -l)
+ nr_of_exist_should_be 9 "$exist" "$FUNCNAME" || return 1
+ nr_of_nonexist_should_be 4 "$nonexist" "$FUNCNAME" || return 1
+ echo "[PASS] $FUNCNAME"
+}
+
+test_smallfile_pfn() {
+ local exist
+ local nonexist
+ create_small_file
+
+ ./fincore -m 0x4 $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ exist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep -v 0x0 | wc -l)
+ nonexist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep 0x0 | wc -l)
+ nr_of_exist_should_be 9 "$exist" "$FUNCNAME" || return 1
+ nr_of_nonexist_should_be 4 "$nonexist" "$FUNCNAME" || return 1
+ echo "[PASS] $FUNCNAME"
+}
+
+test_smallfile_multientry() {
+ local exist
+ local nonexist
+ create_small_file
+
+ ./fincore -m 0x1c -e $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ exist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2,3,4 | grep -vP "0x0\t0x0\t0x0" | wc -l)
+ nonexist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2,3,4 | grep -P "0x0\t0x0\t0x0" | wc -l)
+ nr_of_exist_should_be 9 "$exist" "$FUNCNAME" || return 1
+ nr_of_nonexist_should_be 4 "$nonexist" "$FUNCNAME" || return 1
+ echo "[PASS] $FUNCNAME"
+}
+
+test_smallfile_pfn_skiphole() {
+ local exist
+ local nonexist
+ local nr_entries
+ create_small_file
+
+ ./fincore -m 0x6 -e $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ exist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep -v 0x0 | wc -l)
+ nonexist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep 0x0 | wc -l)
+ nr_entries=$(get_fincore_extra_nr_entries $TMPF/$FUNCNAME)
+ nr_of_exist_should_be 9 "$exist" "$FUNCNAME" || return 1
+ nr_of_nonexist_should_be 4 "$nonexist" "$FUNCNAME" || return 1
+ nr_of_valid_entries_should_be 9 "$nr_entries" "$FUNCNAME" || return 1
+ echo "[PASS] $FUNCNAME"
+}
+
+test_smallfile_pagecache_tag() {
+ local nr_dirty
+ local fincore_extra_tags
+ create_small_file
+
+ # dirty one page
+ date >> $WDIR/smallfile
+
+ ./fincore -m 0x10 -e -t 0xff $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ nr_dirty=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep 0x1 | wc -l)
+ fincore_extra_tags=$(get_fincore_extra_tags $TMPF/$FUNCNAME)
+ if [ "$nr_dirty" -ne 1 ] ; then
+ echo "[FAIL] $FUNCNAME: Number of dirty bit should be 1, but got $nr_dirty"
+ return 1
+ fi
+ if [ "$fincore_extra_tags" != 0x7 ] ; then
+ echo "[FAIL] $FUNCNAME: unsupported PAGECACHE_TAG_* should be ignored."
+ return 1
+ fi
+
+ # ignore only PAGECACHE_TAG_DIRTY
+ ./fincore -m 0x10 -e -t 0x6 $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ nr_dirty=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep 0x1 | wc -l)
+ fincore_extra_tags=$(get_fincore_extra_tags $TMPF/$FUNCNAME)
+ if [ "$nr_dirty" -ne 0 ] ; then
+ echo "[FAIL] $FUNCNAME: Number of dirty bit should be 0, but got $nr_dirty"
+ return 1
+ fi
+ if [ "$fincore_extra_tags" != 0x6 ] ; then
+ echo "[FAIL] $FUNCNAME: unsupported PAGECACHE_TAG_* should be ignored."
+ return 1
+ fi
+ echo "[PASS] $FUNCNAME"
+}
+
+# in-kernel function sys_fincore() repeat copy_to_user() per 256 entries,
+# so testing for large file is meaningful testcase.
+test_largefile_pfn() {
+ local exist
+ local nonexist
+ create_large_file
+
+ ./fincore -m 0x4 -e $WDIR/largefile > $TMPF/$FUNCNAME 2>&1
+ exist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep -v 0x0 | wc -l)
+ nonexist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep 0x0 | wc -l)
+ nr_of_exist_should_be 768 "$exist" "$FUNCNAME" || return 1
+ nr_of_nonexist_should_be 256 "$nonexist" "$FUNCNAME" || return 1
+ echo "[PASS] $FUNCNAME"
+}
+
+test_largefile_pfn_offset() {
+ local exist
+ local nonexist
+ create_large_file
+
+ ./fincore -m 0x4 -s 0x80000 $WDIR/largefile > $TMPF/$FUNCNAME 2>&1
+ exist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep -v 0x0 | wc -l)
+ nonexist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep 0x0 | wc -l)
+ nr_of_exist_should_be 640 "$exist" "$FUNCNAME" || return 1
+ nr_of_nonexist_should_be 256 "$nonexist" "$FUNCNAME" || return 1
+ echo "[PASS] $FUNCNAME"
+}
+
+test_largefile_pfn_overrun() {
+ local exist
+ local nonexist
+ local nr_entries
+ create_large_file
+
+ ./fincore -m 0x4 -s 0x80000 -l 0x400000 -e $WDIR/largefile > $TMPF/$FUNCNAME 2>&1
+ exist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep -v 0x0 | wc -l)
+ nonexist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep 0x0 | wc -l)
+ nr_entries=$(get_fincore_extra_nr_entries $TMPF/$FUNCNAME)
+ nr_of_exist_should_be 640 "$exist" "$FUNCNAME" || return 1
+ nr_of_nonexist_should_be 384 "$nonexist" "$FUNCNAME" || return 1
+ nr_of_valid_entries_should_be 896 "$nr_entries" "$FUNCNAME" || return 1
+ echo "[PASS] $FUNCNAME"
+}
+
+test_largefile_pfn_skiphole() {
+ local exist
+ local nonexist
+ create_large_file
+
+ ./fincore -m 0x6 -s 0x100000 -l 0x102000 -e $WDIR/largefile > $TMPF/$FUNCNAME 2>&1
+ exist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep -v 0x0 | wc -l)
+ nonexist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep 0x0 | wc -l)
+ nr_entries=$(get_fincore_extra_nr_entries $TMPF/$FUNCNAME)
+ nr_of_exist_should_be 258 "$exist" "$FUNCNAME" || return 1
+ nr_of_nonexist_should_be 0 "$nonexist" "$FUNCNAME" || return 1
+ nr_of_valid_entries_should_be 258 "$nr_entries" "$FUNCNAME" || return 1
+ echo "[PASS] $FUNCNAME"
+}
+
+test_tmpfs_pfn() {
+ local exist
+ local nonexist
+ create_tmpfs_file
+
+ ./fincore -m 0x4 /tmp/tmpfile > $TMPF/$FUNCNAME 2>&1
+ exist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep -v 0x0 | wc -l)
+ nonexist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep 0x0 | wc -l)
+ nr_of_exist_should_be 9 "$exist" "$FUNCNAME" || return 1
+ nr_of_nonexist_should_be 4 "$nonexist" "$FUNCNAME" || return 1
+ echo "[PASS] $FUNCNAME"
+}
+
+test_hugetlb_pfn() {
+ local exist
+ local nonexist
+ local exitcode=0
+ create_hugetlb_file
+ if [ $? -ne 0 ] ; then
+ echo "[FAIL] $FUNCNAME: fail to create a file on hugetlbfs"
+ return 1
+ fi
+ local hugepagesize=$[$(cat /proc/meminfo | grep Hugepagesize: | tr -s ' ' | cut -f2 -d' ') * 1024]
+ ./fincore -p $hugepagesize -m 0x4 -e $WDIR/hugepages/file > $TMPF/$FUNCNAME 2>&1
+ exist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep -v 0x0 | wc -l)
+ nonexist=$(get_buffer $TMPF/$FUNCNAME | cut -f 2 | grep 0x0 | wc -l)
+ nr_entries=$(get_fincore_extra_nr_entries $TMPF/$FUNCNAME)
+ nr_of_exist_should_be 6 "$exist" "$FUNCNAME" || return 1
+ nr_of_nonexist_should_be 4 "$nonexist" "$FUNCNAME" || return 1
+ nr_of_valid_entries_should_be 10 "$nr_entries" "$FUNCNAME" || return 1
+ rm -rf $WDIR/hugepages/file
+ echo "[PASS] $FUNCNAME"
+}
+
+test_invalid_start_address() {
+ create_small_file
+ ./fincore -m 0x4 -s -0x4000 -l 1 -e $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ if [ $? -eq 0 ] || ! check_einval $TMPF/$FUNCNAME ; then
+ echo "[FAIL] $FUNCNAME: negative start is invalid"
+ return 1
+ fi
+ ./fincore -m 0x4 -s 0x100000 -l 1 -e $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ if [ $? -eq 0 ] || ! check_einval $TMPF/$FUNCNAME ; then
+ echo "[FAIL] $FUNCNAME: too large start is invalid"
+ return 1
+ fi
+ ./fincore -m 0x4 -s 0x30 $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ if [ $? -eq 0 ] || ! check_einval $TMPF/$FUNCNAME ; then
+ echo "[FAIL] $FUNCNAME: fincore should fail for unaligned start address"
+ return 1
+ fi
+ echo "[PASS] $FUNCNAME"
+}
+
+test_invalid_len() {
+ create_small_file
+ ./fincore -m 0x4 -l 0 $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ if [ $? -eq 0 ] || ! check_einval $TMPF/$FUNCNAME ; then
+ echo "[FAIL] $FUNCNAME: zero len is invalid"
+ return 1
+ fi
+ ./fincore -m 0x4 -l -10 $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ if [ $? -eq 0 ] || ! check_einval $TMPF/$FUNCNAME ; then
+ echo "[FAIL] $FUNCNAME: negative len is invalid"
+ return 1
+ fi
+ echo "[PASS] $FUNCNAME"
+}
+
+test_invalid_mode() {
+ create_small_file
+ ./fincore -m 0x0 $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ if [ $? -eq 0 ] || ! check_einval $TMPF/$FUNCNAME ; then
+ echo "[FAIL] $FUNCNAME: mode == NULL is invalid mode"
+ return 1
+ fi
+ ./fincore -m 0x5 $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ if [ $? -eq 0 ] || ! check_einval $TMPF/$FUNCNAME ; then
+ echo "[FAIL] $FUNCNAME: mode == (FINCORE_BMAP|FINCORE_PFN) is invalid mode"
+ return 1
+ fi
+ ./fincore -m 0x3 $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ if [ $? -eq 0 ] || ! check_einval $TMPF/$FUNCNAME ; then
+ echo "[FAIL] $FUNCNAME: mode == (FINCORE_BMAP|FINCORE_PGOFF) is invalid mode"
+ return 1
+ fi
+ ./fincore -m 0x6 $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "[FAIL] $FUNCNAME: mode == (FINCORE_PGOFF|FINCORE_PFN) is valid mode"
+ return 1
+ fi
+ ./fincore -m 0x2 $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "[FAIL] $FUNCNAME: mode == (FINCORE_PGOFF) is valid mode"
+ return 1
+ fi
+ ./fincore -m 0x1004 $WDIR/smallfile > $TMPF/$FUNCNAME 2>&1
+ if [ $? -eq 0 ] || ! check_einval $TMPF/$FUNCNAME ; then
+ echo "[FAIL] $FUNCNAME: mode == (Unknown|FINCORE_PFN) is invalid mode"
+ return 1
+ fi
+ echo "[PASS] $FUNCNAME"
+}
+
+test_unaligned_start_address_hugetlb() {
+ local exist
+ local nonexist
+ local exitcode=0
+ create_hugetlb_file
+ if [ $? -ne 0 ] ; then
+ echo "[FAIL] $FUNCNAME: fail to create a file on hugetlbfs"
+ return 1
+ fi
+ local hugepagesize=$[$(cat /proc/meminfo | grep Hugepagesize: | tr -s ' ' | cut -f2 -d' ') * 1024]
+ ./fincore -p $hugepagesize -m 0x4 -s 0x1000 $WDIR/hugepages/file > $TMPF/$FUNCNAME 2>&1
+ if [ $? -eq 0 ] || ! check_einval $TMPF/$FUNCNAME ; then
+ echo "[FAIL] $FUNCNAME: fincore should fail for page-unaligned start address"
+ return 1
+ fi
+ ./fincore -p $hugepagesize -m 0x4 -s $hugepagesize $WDIR/hugepages/file > $TMPF/$FUNCNAME 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "[FAIL] $FUNCNAME: fincore should pass for hugepage-aligned start address"
+ return 1
+ fi
+ echo "[PASS] $FUNCNAME"
+}
+
+test_smallfile_bytemap || abort
+test_smallfile_pfn || abort
+test_smallfile_multientry || abort
+test_smallfile_pfn_skiphole || abort
+test_smallfile_pagecache_tag || abort
+test_largefile_pfn || abort
+test_largefile_pfn_offset || abort
+test_largefile_pfn_overrun || abort
+test_largefile_pfn_skiphole || abort
+test_tmpfs_pfn || abort
+test_hugetlb_pfn || abort
+test_invalid_start_address || abort
+test_invalid_len || abort
+test_invalid_mode || abort
+test_unaligned_start_address_hugetlb || abort
+
+# cleanup
+rm -rf $WDIR/hugepages/file
+umount $WDIR/hugepages > /dev/null 2>&1
+
+exit 0
--
1.9.3
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* [PATCH v2 2/4] mm: introduce fincore()
From: Naoya Horiguchi @ 2014-07-03 21:52 UTC (permalink / raw)
To: Andrew Morton
Cc: Konstantin Khlebnikov, Wu Fengguang, Arnaldo Carvalho de Melo,
Borislav Petkov, Kirill A. Shutemov, Johannes Weiner,
Rusty Russell, David Miller, Andres Freund, linux-kernel,
linux-mm, Dave Hansen, Christoph Hellwig, Michael Kerrisk,
Linux API, Naoya Horiguchi
In-Reply-To: <1404424335-30128-1-git-send-email-n-horiguchi@ah.jp.nec.com>
This patch provides a new system call fincore(2), which provides mincore()-
like information, i.e. page residency of a given file. But unlike mincore(),
fincore() has a mode flag which allows us to extract detailed information
about page cache like pfn and page flag. This kind of information is very
helpful, for example when applications want to know the file cache status
to control the IO on their own way.
The details about the data format being passed to userspace are explained
in inline comment, but generally in long entry format, we can choose which
information is extraced flexibly, so you don't have to waste memory by
extracting unnecessary information. And with FINCORE_PGOFF flag, we can skip
hole pages (not on memory,) which makes us avoid a flood of meaningless
zero entries when calling on extremely large (but only few pages of it
are loaded on memory) file.
Basic testset is added in the next patch on tools/testing/selftests/fincore/.
ChangeLog v2:
- move definition of FINCORE_* to include/uapi/linux/pagecache.h
- add another parameter fincore_extra to sys_fincore()
- rename FINCORE_SKIP_HOLE to FINCORE_PGOFF and change bit order.
- add valid argument check (start should be inside file address range,
nr_pages should be positive)
- add end-of-file check (scan to the end of file even if the last page
is a hole)
- add access_ok(VERIFY_WIRTE) (copied from mincore())
- update inline comments
Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
---
arch/x86/syscalls/syscall_64.tbl | 1 +
include/linux/syscalls.h | 4 +
include/uapi/linux/pagecache.h | 94 ++++++++++++
mm/Makefile | 2 +-
mm/fincore.c | 322 +++++++++++++++++++++++++++++++++++++++
5 files changed, 422 insertions(+), 1 deletion(-)
create mode 100644 mm/fincore.c
diff --git v3.16-rc3.orig/arch/x86/syscalls/syscall_64.tbl v3.16-rc3/arch/x86/syscalls/syscall_64.tbl
index ec255a1646d2..9d291b7081ca 100644
--- v3.16-rc3.orig/arch/x86/syscalls/syscall_64.tbl
+++ v3.16-rc3/arch/x86/syscalls/syscall_64.tbl
@@ -323,6 +323,7 @@
314 common sched_setattr sys_sched_setattr
315 common sched_getattr sys_sched_getattr
316 common renameat2 sys_renameat2
+317 common fincore sys_fincore
#
# x32-specific system call numbers start at 512 to avoid cache impact
diff --git v3.16-rc3.orig/include/linux/syscalls.h v3.16-rc3/include/linux/syscalls.h
index b0881a0ed322..60795ee8f9ee 100644
--- v3.16-rc3.orig/include/linux/syscalls.h
+++ v3.16-rc3/include/linux/syscalls.h
@@ -65,6 +65,7 @@ struct old_linux_dirent;
struct perf_event_attr;
struct file_handle;
struct sigaltstack;
+struct fincore_extra;
#include <linux/types.h>
#include <linux/aio_abi.h>
@@ -866,4 +867,7 @@ asmlinkage long sys_process_vm_writev(pid_t pid,
asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type,
unsigned long idx1, unsigned long idx2);
asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags);
+asmlinkage long sys_fincore(int fd, loff_t start, long nr_pages,
+ int mode, unsigned char __user *vec,
+ struct fincore_extra __user *extra);
#endif
diff --git v3.16-rc3.orig/include/uapi/linux/pagecache.h v3.16-rc3/include/uapi/linux/pagecache.h
index 15e879f7395f..cd53c69d6f56 100644
--- v3.16-rc3.orig/include/uapi/linux/pagecache.h
+++ v3.16-rc3/include/uapi/linux/pagecache.h
@@ -14,4 +14,98 @@ enum {
#define PAGECACHE_TAG_MASK ((1UL << __NR_PAGECACHE_TAGS) - 1)
+/*
+ * You can control how the buffer in userspace is filled with this mode
+ * parameters:
+ *
+ * - FINCORE_BMAP:
+ * the page status is returned in a vector of bytes.
+ * The least significant bit of each byte is 1 if the referenced page
+ * is in memory, otherwise it is zero.
+ *
+ * - FINCORE_PGOFF:
+ * if this flag is set, fincore() doesn't store any information about
+ * holes. Instead each records per page has the entry of page offset,
+ * using 8 bytes. This mode is useful if we handle a large file and
+ * only few pages are on memory.
+ *
+ * - FINCORE_PFN:
+ * stores pfn, using 8 bytes.
+ *
+ * - FINCORE_PAGEFLAGS:
+ * stores page flags, using 8 bytes. See definition of KPF_* for
+ * details of each bit.
+ *
+ * - FINCORE_PAGECACHE_TAGS:
+ * stores pagecache tags, using 8 bytes. See definition of PAGECACHE_TAG_*
+ * for details of each bit.
+ *
+ * FINCORE_BMAP shouldn't be used combined with any other flags, and returnd
+ * data in this mode is like this:
+ *
+ * page offset 0 1 2 3 4
+ * +---+---+---+---+---+
+ * | 1 | 0 | 0 | 1 | 1 | ...
+ * +---+---+---+---+---+
+ * <->
+ * 1 byte
+ *
+ * For FINCORE_PFN, page data is formatted like this:
+ *
+ * page offset 0 1 2 3 4
+ * +-------+-------+-------+-------+-------+
+ * | pfn | pfn | pfn | pfn | pfn | ...
+ * +-------+-------+-------+-------+-------+
+ * <----->
+ * 8 byte
+ *
+ * We can use multiple flags among the flags in FINCORE_LONGENTRY_MASK.
+ * For example, when the mode is FINCORE_PFN|FINCORE_PAGEFLAGS, the per-page
+ * information is stored like this:
+ *
+ * page offset 0 page offset 1 page offset 2 page offset 3
+ * (hole)
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * | pfn | flags | pfn | flags | 0 | 0 | pfn | flags | ...
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * <-------------> <-------------> <-------------> <------------->
+ * 16 bytes 16 bytes 16 bytes 16 bytes
+ *
+ * When FINCORE_PGOFF is set, we store page offset entry and ignore holes
+ * For example, the data format of mode FINCORE_PGOFF|FINCORE_PFN|
+ * FINCORE_PAGEFLAGS|FINCORE_PAGECACHE_TAGS is like follows:
+ *
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * | pgoff | pfn | flags | tags | pgoff | pfn | flags | tags | ...
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * <-----------------------------> <----------------------------->
+ * 32 bytes 32 bytes
+ */
+#define FINCORE_BMAP 0x01 /* bytemap mode */
+#define FINCORE_PGOFF 0x02
+#define FINCORE_PFN 0x04
+#define FINCORE_PAGE_FLAGS 0x08
+#define FINCORE_PAGECACHE_TAGS 0x10
+
+#define FINCORE_MODE_MASK 0x1f
+#define FINCORE_LONGENTRY_MASK (FINCORE_PGOFF | FINCORE_PFN | \
+ FINCORE_PAGE_FLAGS | FINCORE_PAGECACHE_TAGS)
+
+struct fincore_extra {
+ /*
+ * (output) the number of entries with valid data, this is useful
+ * if you set FINCORE_PGOFF and want to know the end of filled data.
+ */
+ unsigned long nr_entries;
+
+ /*
+ * (input) A mask of pagecache tags which selects what fields the
+ * user wants.
+ * (output) A mask of pagecache tags returned from the kernel
+ * which tells userspace which data it actually filled.
+ * This variable is used only when FINCORE_PAGECACHE_TAGS is set.
+ */
+ unsigned long tags;
+};
+
#endif /* _UAPI_LINUX_PAGECACHE_H */
diff --git v3.16-rc3.orig/mm/Makefile v3.16-rc3/mm/Makefile
index 4064f3ec145e..cc9420221afd 100644
--- v3.16-rc3.orig/mm/Makefile
+++ v3.16-rc3/mm/Makefile
@@ -18,7 +18,7 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \
mm_init.o mmu_context.o percpu.o slab_common.o \
compaction.o balloon_compaction.o vmacache.o \
interval_tree.o list_lru.o workingset.o \
- iov_iter.o $(mmu-y)
+ iov_iter.o fincore.o $(mmu-y)
obj-y += init-mm.o
diff --git v3.16-rc3.orig/mm/fincore.c v3.16-rc3/mm/fincore.c
new file mode 100644
index 000000000000..df7226658c0d
--- /dev/null
+++ v3.16-rc3/mm/fincore.c
@@ -0,0 +1,322 @@
+/*
+ * fincore(2) system call
+ *
+ * Copyright (C) 2014 NEC Corporation, Naoya Horiguchi
+ */
+
+#include <linux/syscalls.h>
+#include <linux/pagemap.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/hugetlb.h>
+#include <uapi/linux/pagecache.h>
+
+
+struct fincore_control {
+ int mode;
+ int width; /* width of each entry (in bytes) */
+ unsigned char *buffer;
+ long buffer_size;
+ void *cursor; /* current position on the buffer */
+ pgoff_t pgstart; /* start point of page cache scan in each run
+ * of the while loop */
+ long nr_pages; /* number of pages to be copied to userspace
+ * (decreasing while scan proceeds) */
+ long scanned_offset; /* page offset of the lastest scanned page */
+ unsigned long tags; /* pagecache tag mask */
+ struct address_space *mapping;
+};
+
+/*
+ * TODO: doing radix_tree_tag_get() for each tag is not optimal, but no easy
+ * way without degrading finely tuned radix tree routines.
+ */
+static unsigned long get_pagecache_tags(struct fincore_control *fc,
+ unsigned long index)
+{
+ int i;
+ unsigned long tags = 0;
+ struct radix_tree_root *root = &fc->mapping->page_tree;
+
+ for (i = 0; i < __NR_PAGECACHE_TAGS; i++) {
+ if (fc->tags & (1UL << i))
+ if (radix_tree_tag_get(root, index, i))
+ tags |= 1 << i;
+ }
+ return tags;
+}
+
+#define store_entry(fc, type, data) ({ \
+ *(type *)fc->cursor = (type)data; \
+ fc->cursor += sizeof(type); \
+})
+
+/*
+ * Store page cache data to temporal buffer in the specified format depending
+ * on fincore mode.
+ */
+static void __do_fincore(struct fincore_control *fc, struct page *page,
+ unsigned long index)
+{
+ VM_BUG_ON(!page);
+ VM_BUG_ON((unsigned long)fc->cursor - (unsigned long)fc->buffer
+ >= fc->buffer_size);
+ if (fc->mode & FINCORE_BMAP)
+ store_entry(fc, unsigned char, PageUptodate(page));
+ else if (fc->mode & (FINCORE_LONGENTRY_MASK)) {
+ if (fc->mode & FINCORE_PGOFF)
+ store_entry(fc, unsigned long, index);
+ if (fc->mode & FINCORE_PFN)
+ store_entry(fc, unsigned long, page_to_pfn(page));
+ if (fc->mode & FINCORE_PAGE_FLAGS)
+ store_entry(fc, unsigned long, stable_page_flags(page));
+ if (fc->mode & FINCORE_PAGECACHE_TAGS)
+ store_entry(fc, unsigned long,
+ get_pagecache_tags(fc, index));
+ }
+}
+
+/*
+ * Traverse page cache tree. It's assumed that temporal buffer are zeroed
+ * in advance. Due to this, we don't have to store zero entry explicitly
+ * one-by-one and we just set fc->cursor to the position of the next
+ * on-memory page.
+ *
+ * Return value is the number of pages whose data is stored in fc->buffer.
+ */
+static long do_fincore(struct fincore_control *fc, int nr_pages)
+{
+ pgoff_t pgend = fc->pgstart + nr_pages;
+ struct radix_tree_iter iter;
+ void **slot;
+ long nr = 0;
+
+ fc->cursor = fc->buffer;
+
+ rcu_read_lock();
+restart:
+ radix_tree_for_each_slot(slot, &fc->mapping->page_tree, &iter,
+ fc->pgstart) {
+ long jump;
+ struct page *page;
+
+ fc->scanned_offset = iter.index;
+ /* Handle holes */
+ jump = iter.index - fc->pgstart - nr;
+ if (jump) {
+ if (!(fc->mode & FINCORE_PGOFF)) {
+ if (iter.index < pgend) {
+ fc->cursor += jump * fc->width;
+ nr = iter.index - fc->pgstart;
+ } else {
+ /*
+ * Fill remaining buffer as hole. Next
+ * call should start at offset pgend.
+ */
+ nr = nr_pages;
+ fc->scanned_offset = pgend - 1;
+ break;
+ }
+ }
+ }
+repeat:
+ page = radix_tree_deref_slot(slot);
+ if (unlikely(!page))
+ /*
+ * No need to increment nr and fc->cursor, because next
+ * iteration should detect hole and update them there.
+ */
+ continue;
+ else if (radix_tree_exception(page)) {
+ if (radix_tree_deref_retry(page)) {
+ /*
+ * Transient condition which can only trigger
+ * when entry at index 0 moves out of or back
+ * to root: none yet gotten, safe to restart.
+ */
+ WARN_ON(iter.index);
+ goto restart;
+ }
+ __do_fincore(fc, page, iter.index);
+ } else {
+ if (!page_cache_get_speculative(page))
+ goto repeat;
+
+ /* Has the page moved? */
+ if (unlikely(page != *slot)) {
+ page_cache_release(page);
+ goto repeat;
+ }
+
+ __do_fincore(fc, page, iter.index);
+ page_cache_release(page);
+ }
+
+ if (++nr == nr_pages)
+ break;
+ }
+
+ if (!(fc->mode & FINCORE_PGOFF)) {
+ nr = nr_pages;
+ fc->scanned_offset = pgend - 1;
+ }
+
+ rcu_read_unlock();
+
+ return nr;
+}
+
+static inline bool fincore_validate_mode(int mode)
+{
+ if (mode & ~FINCORE_MODE_MASK)
+ return false;
+ if (!(!!(mode & FINCORE_BMAP) ^ !!(mode & FINCORE_LONGENTRY_MASK)))
+ return false;
+ return true;
+}
+
+#define FINCORE_LOOP_STEP 256L
+
+/*
+ * The fincore(2) system call
+ *
+ * @fd: file descriptor of the target file
+ * @start: starting address offset of the target file (in byte).
+ * This should be aligned to page cache size.
+ * @nr_pages: the number of pages whose data is passed to userspace.
+ * @mode fincore mode flags to determine the entry's format
+ * @vec pointer of the userspace buffer. The size must be equal to or
+ * larger than (@nr_pages * width), where width is the size of
+ * each entry.
+ * @extra used to input/output additional information from/to userspace
+ *
+ * fincore() returns the memory residency status and additional info (like
+ * pfn and page flags) of the given file's pages.
+ *
+ * Depending on the fincore mode, caller can receive the different formatted
+ * information. See the comment on definition of FINCORE_*.
+ *
+ * Because the status of a page can change after fincore() checks it once,
+ * the returned vector may contain stale information.
+ *
+ * return values:
+ * -EBADF: @fd isn't a valid open file descriptor
+ * -EFAULT: @vec points to an illegal address
+ * -EINVAL: @start is unaligned to page cache size or is out of file range.
+ * Or @nr_pages is non-positive. Or @mode is invalid.
+ * Or fincore_extra is not given in FINCORE_PAGECACHE_TAG mode.
+ * 0: fincore() is successfully done
+ */
+SYSCALL_DEFINE6(fincore, int, fd, loff_t, start, long, nr_pages,
+ int, mode, unsigned char __user *, vec,
+ struct fincore_extra __user *, extra)
+{
+ long ret = 0;
+ long step;
+ long nr = 0;
+ long pages_to_eof;
+ int pc_shift = PAGE_CACHE_SHIFT;
+ struct fd f;
+
+ struct fincore_control fc = {
+ .mode = mode,
+ .width = sizeof(unsigned char),
+ };
+
+ if (start < 0 || nr_pages <= 0)
+ return -EINVAL;
+
+ if (!fincore_validate_mode(mode))
+ return -EINVAL;
+
+ f = fdget(fd);
+
+ if (is_file_hugepages(f.file))
+ pc_shift = huge_page_shift(hstate_file(f.file));
+
+ if (!IS_ALIGNED(start, 1 << pc_shift)) {
+ ret = -EINVAL;
+ goto fput;
+ }
+
+ /*
+ * TODO: support /dev/mem, /proc/pid/mem for system/process wide
+ * page survey, which would obsolete /proc/kpageflags, and
+ * /proc/pid/pagemap.
+ */
+ if (!S_ISREG(file_inode(f.file)->i_mode)) {
+ ret = -EBADF;
+ goto fput;
+ }
+
+ fc.pgstart = start >> pc_shift;
+ pages_to_eof = DIV_ROUND_UP(i_size_read(file_inode(f.file)),
+ 1UL << pc_shift) - fc.pgstart;
+ /* start is too large */
+ if (pages_to_eof <= 0) {
+ ret = -EINVAL;
+ goto fput;
+ }
+ /* Never go beyond the end of file */
+ fc.nr_pages = min(pages_to_eof, nr_pages);
+ fc.mapping = f.file->f_mapping;
+ if (mode & FINCORE_LONGENTRY_MASK)
+ fc.width = ((mode & FINCORE_PGOFF ? 1 : 0) +
+ (mode & FINCORE_PFN ? 1 : 0) +
+ (mode & FINCORE_PAGE_FLAGS ? 1 : 0) +
+ (mode & FINCORE_PAGECACHE_TAGS ? 1 : 0)
+ ) * sizeof(unsigned long);
+
+ if (mode & FINCORE_PAGECACHE_TAGS) {
+ if (!extra) {
+ ret = -EINVAL;
+ goto fput;
+ } else {
+ fc.tags = extra->tags & PAGECACHE_TAG_MASK;
+ __put_user(fc.tags, &extra->tags);
+ }
+ }
+
+ if (!access_ok(VERIFY_WRITE, vec, nr_pages * fc.width)) {
+ ret = -EFAULT;
+ goto fput;
+ }
+
+ step = min(fc.nr_pages, FINCORE_LOOP_STEP);
+
+ fc.buffer_size = step * fc.width;
+ fc.buffer = kmalloc(fc.buffer_size, GFP_TEMPORARY);
+ if (!fc.buffer) {
+ ret = -ENOMEM;
+ goto fput;
+ }
+
+ while (fc.nr_pages > 0) {
+ memset(fc.buffer, 0, fc.buffer_size);
+ ret = do_fincore(&fc, min(step, fc.nr_pages));
+ /* Reached the end of the file */
+ if (ret == 0)
+ break;
+ if (ret < 0)
+ break;
+ if (copy_to_user(vec + nr * fc.width,
+ fc.buffer, ret * fc.width)) {
+ ret = -EFAULT;
+ break;
+ }
+ fc.nr_pages -= ret;
+ fc.pgstart = fc.scanned_offset + 1;
+ nr += ret;
+ }
+
+ kfree(fc.buffer);
+
+ if (extra)
+ __put_user(nr, &extra->nr_entries);
+
+fput:
+ fdput(f);
+ return ret;
+}
--
1.9.3
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* [PATCH v2 1/4] define PAGECACHE_TAG_* as enumeration under include/uapi
From: Naoya Horiguchi @ 2014-07-03 21:52 UTC (permalink / raw)
To: Andrew Morton
Cc: Konstantin Khlebnikov, Wu Fengguang, Arnaldo Carvalho de Melo,
Borislav Petkov, Kirill A. Shutemov, Johannes Weiner,
Rusty Russell, David Miller, Andres Freund, linux-kernel,
linux-mm, Dave Hansen, Christoph Hellwig, Michael Kerrisk,
Linux API, Naoya Horiguchi
In-Reply-To: <1404424335-30128-1-git-send-email-n-horiguchi@ah.jp.nec.com>
We need the pagecache tags to be exported to userspace later in this
series for fincore(2), so this patch moves the definition to the new
include file for preparation. We also use the number of pagecache tags,
so this patch also adds it.
Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
---
include/linux/fs.h | 9 +--------
include/uapi/linux/pagecache.h | 17 +++++++++++++++++
2 files changed, 18 insertions(+), 8 deletions(-)
create mode 100644 include/uapi/linux/pagecache.h
diff --git v3.16-rc3.orig/include/linux/fs.h v3.16-rc3/include/linux/fs.h
index e11d60cc867b..ae4a953bd5f3 100644
--- v3.16-rc3.orig/include/linux/fs.h
+++ v3.16-rc3/include/linux/fs.h
@@ -32,6 +32,7 @@
#include <asm/byteorder.h>
#include <uapi/linux/fs.h>
+#include <uapi/linux/pagecache.h>
struct export_operations;
struct hd_geometry;
@@ -446,14 +447,6 @@ struct block_device {
struct mutex bd_fsfreeze_mutex;
};
-/*
- * Radix-tree tags, for tagging dirty and writeback pages within the pagecache
- * radix trees
- */
-#define PAGECACHE_TAG_DIRTY 0
-#define PAGECACHE_TAG_WRITEBACK 1
-#define PAGECACHE_TAG_TOWRITE 2
-
int mapping_tagged(struct address_space *mapping, int tag);
/*
diff --git v3.16-rc3.orig/include/uapi/linux/pagecache.h v3.16-rc3/include/uapi/linux/pagecache.h
new file mode 100644
index 000000000000..15e879f7395f
--- /dev/null
+++ v3.16-rc3/include/uapi/linux/pagecache.h
@@ -0,0 +1,17 @@
+#ifndef _UAPI_LINUX_PAGECACHE_H
+#define _UAPI_LINUX_PAGECACHE_H
+
+/*
+ * Radix-tree tags, for tagging dirty and writeback pages within the pagecache
+ * radix trees.
+ */
+enum {
+ PAGECACHE_TAG_DIRTY,
+ PAGECACHE_TAG_WRITEBACK,
+ PAGECACHE_TAG_TOWRITE,
+ __NR_PAGECACHE_TAGS,
+};
+
+#define PAGECACHE_TAG_MASK ((1UL << __NR_PAGECACHE_TAGS) - 1)
+
+#endif /* _UAPI_LINUX_PAGECACHE_H */
--
1.9.3
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* [PATCH 0/4] mm: introduce fincore() v2
From: Naoya Horiguchi @ 2014-07-03 21:52 UTC (permalink / raw)
To: Andrew Morton
Cc: Konstantin Khlebnikov, Wu Fengguang, Arnaldo Carvalho de Melo,
Borislav Petkov, Kirill A. Shutemov, Johannes Weiner,
Rusty Russell, David Miller, Andres Freund, linux-kernel,
linux-mm, Dave Hansen, Christoph Hellwig, Michael Kerrisk,
Linux API, Naoya Horiguchi
This is the 2nd version of fincore patchset.
In the previous discussion[1], I got many feedbacks about the following
points:
- robust ABI handling is needed (especially about PAGECACHE_TAG_*)
- man page is necessary
- the parameter/return value of sys_fincore() needs improvement
- the order of bits FINCORE_* and the order of 8 bytes entry in buffer
should be identical
so I covered these in this version.
Any comments/reviews are welcomed.
[1] http://lwn.net/Articles/601020/
Thanks,
Naoya Horiguchi
---
Tree: git@github.com:Naoya-Horiguchi/linux.git
Branch: v3.16-rc3/fincore.ver2
---
Summary:
Naoya Horiguchi (4):
define PAGECACHE_TAG_* as enumeration under include/uapi
mm: introduce fincore()
selftests/fincore: add test code for fincore()
man2/fincore.2: document general description about fincore(2)
arch/x86/syscalls/syscall_64.tbl | 1 +
include/linux/fs.h | 9 +-
include/linux/syscalls.h | 4 +
include/uapi/linux/pagecache.h | 111 ++++++
man2/fincore.2 | 383 ++++++++++++++++++++
mm/Makefile | 2 +-
mm/fincore.c | 322 +++++++++++++++++
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/fincore/Makefile | 31 ++
.../selftests/fincore/create_hugetlbfs_file.c | 49 +++
tools/testing/selftests/fincore/fincore.c | 166 +++++++++
tools/testing/selftests/fincore/run_fincoretests | 401 +++++++++++++++++++++
12 files changed, 1471 insertions(+), 9 deletions(-)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [RFC PATCH 00/11] Adding FreeBSD's Capsicum security framework (part 1)
From: David Drysdale @ 2014-07-03 18:39 UTC (permalink / raw)
To: Paolo Bonzini
Cc: LSM List, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Greg Kroah-Hartman, Alexander Viro, Meredydd Luff, Kees Cook,
James Morris, Linux API, qemu-devel
In-Reply-To: <53B51E81.4090700-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
On Thu, Jul 03, 2014 at 11:12:33AM +0200, Paolo Bonzini wrote:
> Il 30/06/2014 12:28, David Drysdale ha scritto:
> >Hi all,
> >
> >The last couple of versions of FreeBSD (9.x/10.x) have included the
> >Capsicum security framework [1], which allows security-aware
> >applications to sandbox themselves in a very fine-grained way. For
> >example, OpenSSH now (>= 6.5) uses Capsicum in its FreeBSD version to
> >restrict sshd's credentials checking process, to reduce the chances of
> >credential leakage.
>
> Hi David,
>
> we've had similar goals in QEMU. QEMU can be used as a virtual
> machine monitor from the command line, but it also has an API that
> lets a management tool drive QEMU via AF_UNIX sockets. Long term,
> we would like to have a restricted mode for QEMU where all file
> descriptors are obtained via SCM_RIGHTS or /dev/fd, and syscalls can
> be locked down.
>
> Currently we do use seccomp v2 BPF filters, but unfortunately this
> didn't help very much. QEMU supports hotplugging hence the filter
> must whitelist anything that _might_ be used in the future, which is
> generally... too much.
>
> Something like Capsicum would be really nice because it attaches
> capabilities to file descriptors. However, I wonder however how
> extensible Capsicum could be, and I am worried about the
> proliferation of capabilities that its design naturally leads to.
True, capability rights are likely to expand over time (although
FreeBSD only expanded from 55 to 60 between 9.x and 10.x).
> Given Linux's previous experience with BPF filters, what do you
> think about attaching specific BPF programs to file descriptors?
> Then whenever a syscall is run that affects a file descriptor, the
> BPF program for the file descriptor (attached to a struct file* as
> in Capsicum) would run in addition to the process-wide filter.
That sounds kind of clever, but also kind of complicated.
Off the top of my head, one particular problem is that not all
fd->struct file conversions in the kernel are completely specified
by an enclosing syscall and the explicit values of its parameters.
For example, the actual contents of the arguments to io_submit(2)
aren't visible to a seccomp-bpf program (as it can't read the __user
memory for the iocb structures), and so it can't distinguish a
read from a write.
Also, there could potentially be some odd interactions with file
descriptors passed between processes, if the BPF program relies
on assumptions about the environment of the original process. For
example, what happens if an x86_64 process passes a filter-attached
FD to an ia32 process? Given that the syscall numbers are
arch-specific, I guess that means the filter program would have
to include arch-specific branches for any possible variant.
More generally, I suspect that keeping things simpler will end
up being more secure. Capsicum was based on well-studied ideas
from the world of object capability-based security, and I'd be
nervous about adding complications that take us further away from
that.
> An equivalent of PR_SET_NO_NEW_PRIVS can also be added to file
> descriptors, so that a program that doesn't lock down syscalls can
> still lock down the operations (including fcntls and ioctls) on
> specific file descriptors.
>
> Converting FreeBSD capabilities to BPF programs can be easily
> implemented in userspace.
I get the idea, but I'm not sure it would be that easy! The
BPF-generation library would need to hold all of the mappings
from system calls (and their arguments) to the equivalent
required rights -- and vice versa.
That mapping would also need be kept closely in sync with the kernel
and other system libraries -- if a new syscall is added and libc (or
some other library) started using it, the equivalent BPF chunks would
need to be updated to cope.
> > [Capsicum also includes 'capability mode', which locks down the
> > available syscalls so the rights restrictions can't just be bypassed
> > by opening new file descriptors; I'll describe that separately later.]
>
> This can also be implemented in userspace via seccomp and
> PR_SET_NO_NEW_PRIVS.
Well, mostly (and in fact I've got an attempt to do exactly that at
https://github.com/google/capsicum-test/blob/dev/linux-bpf-capmode.c).
But there are a few wrinkles with that approach.
First, we need Kees Cook's patches to allow seccomp filters
to be synchronized across existing threads, but hopefully they
will make it in soon.
Next, there's one awkward syscall case. In capability mode we'd like
to prevent processes from sending signals with kill(2)/tgkill(2)
to other processes, but they should still be able to send themselves
signals. For example, abort(3) generates:
tgkill(gettid(), gettid(), SIGABRT)
Only allowing kill(self) is hard to encode in a seccomp-bpf program, at
least in a way that survives forking.
Finally, capability mode also turns on strict-relative lookups
process-wide; in other words, every openat(dfd, ...) operation
acts as though it has the O_BENEATH_ONLY flag set, regardless of
whether the dfd is a Capsicum capability. I can't see a way to
do that with a BPF program (although it would be possible to add
a filter that polices the requirement to include O_BENEATH_ONLY
rather than implicitly adding it).
So although a capability-mode implementation in terms of seccomp-bpf
is tantalizingly close, at the moment I've got it implemented as a new
seccomp mode.
> > [Policing the rights checks anywhere else, for example at the system
> > call boundary, isn't a good idea because it opens up the possibility
> > of time-of-check/time-of-use (TOCTOU) attacks [2] where FDs are
> > changed (as openat/close/dup2 are allowed in capability mode) between
> > the 'check' at syscall entry and the 'use' at fget() invocation.]
>
> In the case of BPF filters, I wonder if you could stash the BPF
> "environment" somewhere and then use it at fget() invocation.
> Alternatively, it can be reconstructed at fget() time, similar to
> your introduction of fgetr().
Stashing something at syscall entry to be referred to later always
makes me worry about TOCTOU vulnerabilities, but the details might
be OK in this case (given that no check occurs at syscall entry)...
> Thanks,
>
> Paolo
Many thanks for taking the time to comment and think of innovative
ideas!
David
^ permalink raw reply
* Re: [PATCH RFC net-next 08/14] bpf: add eBPF verifier
From: Alexei Starovoitov @ 2014-07-03 17:41 UTC (permalink / raw)
To: David Laight
Cc: Chema Gonzalez, David S. Miller, Ingo Molnar, Linus Torvalds,
Steven Rostedt, Daniel Borkmann, Eric Dumazet, Peter Zijlstra,
Arnaldo Carvalho de Melo, Jiri Olsa, Thomas Gleixner,
H. Peter Anvin, Andrew Morton, Kees Cook, Linux API,
Network Development, LKML
In-Reply-To: <063D6719AE5E284EB5DD2968C1650D6D1726BF1A@AcuExch.aculab.com>
On Thu, Jul 3, 2014 at 2:13 AM, David Laight <David.Laight@aculab.com> wrote:
> From: Alexei Starovoitov
>> >> +#define _(OP) ({ int ret = OP; if (ret < 0) return ret; })
>> > +1 to removing the _ macro. If you want to avoid the 3 lines (is there
>> > anything in the style guide against "if ((err=OP) < 0) ..." ?), at
>>
>> assignment and function call inside 'if' ? I don't like such style.
>>
>> > least use some meaningful macro name (DO_AND_CHECK, or something like
>> > that).
>
> It would have to be RETURN_IF_NEGATIVE().
> But even then it is skipped by searches for 'return'.
try s/\<_\>/RETURN_IF_NEGATIVE/ and see how ugly it looks…
>> Try replacing _ with any other name and see how bad it will look.
>> I tried with MACRO_NAME and with 'if (err) goto' and with 'if (err) return',
>> before I converged on _ macro.
>> I think it's a hidden gem of this patch.
>
> No, it is one of those things that 'seems like a good idea at the time',
> but causes grief much later on.
Disagree. The _ macro in this code has been around for
almost 2 years and survived all sorts of changes all over the verifier.
The macro proved to be very effective in reducing code noise.
> Have you considered saving the error code into 'env' and making most of
> the functions return if an error is set?
> Then the calling code need not check the result of every function call.
that won't work, since err = check1(); err = check2(); if (err) is just wrong,
then err |= check1(); err |= check2() is even worse.
Even if it was possible, continuing verification and printing multiple
errors is too confusing for users. While writing programs and
dealing with verifier rejects we found that the first error is more than
enough to go back and analyze what's wrong with C source.
Notice that verifier prints full verification trace. Without it it was very
hard to understand why particular register at some point has
invalid type.
^ permalink raw reply
* Re: [PATCH v9] mm: support madvise(MADV_FREE)
From: Martin Schwidefsky @ 2014-07-03 16:01 UTC (permalink / raw)
To: Minchan Kim
Cc: Kirill A. Shutemov, Andrew Morton, linux-kernel, linux-mm,
Michael Kerrisk, Linux API, Hugh Dickins, Johannes Weiner,
Rik van Riel, KOSAKI Motohiro, Mel Gorman, Jason Evans,
Zhang Yanfei, Heiko Carstens, linux390, Gerald Schaefer
In-Reply-To: <20140703083729.GE2939@bbox>
On Thu, 3 Jul 2014 17:37:29 +0900
Minchan Kim <minchan@kernel.org> wrote:
> Hello,
>
> On Thu, Jul 03, 2014 at 10:29:01AM +0200, Martin Schwidefsky wrote:
> > On Thu, 3 Jul 2014 16:29:54 +0900
> > Minchan Kim <minchan@kernel.org> wrote:
> >
> > > Hello,
> > >
> > > On Thu, Jul 03, 2014 at 10:03:19AM +0900, Minchan Kim wrote:
> > > > Hello,
> > > >
> > > > On Tue, Jul 01, 2014 at 05:50:58PM +0300, Kirill A. Shutemov wrote:
> > > > > On Tue, Jul 01, 2014 at 09:36:15AM +0900, Minchan Kim wrote:
> > > > > > + do {
> > > > > > + /*
> > > > > > + * XXX: We can optimize with supporting Hugepage free
> > > > > > + * if the range covers.
> > > > > > + */
> > > > > > + next = pmd_addr_end(addr, end);
> > > > > > + if (pmd_trans_huge(*pmd))
> > > > > > + split_huge_page_pmd(vma, addr, pmd);
> > > > >
> > > > > Could you implement proper THP support before upstreaming the feature?
> > > > > It shouldn't be a big deal.
> > > >
> > > > Okay, Hope to review.
> > > >
> > > > Thanks for the feedback!
> > > >
> > >
> > > I tried to implement it but had a issue.
> > >
> > > I need pmd_mkold, pmd_mkclean for MADV_FREE operation and pmd_dirty for
> > > page_referenced. When I investigate all of arches supported THP,
> > > it's not a big deal but s390 is not sure to me who has no idea of
> > > soft tracking of s390 by storage key instead of page table information.
> > > Cced s390 maintainer. Hope to help.
> >
> > Storage key for dirty and referenced tracking is a thing of the past.
> > The current code for s390 uses software tracking for dirty and referenced.
> > There is one catch though, for ptes the software implementation covers
> > dirty and referenced bit but for pmds only referenced bit is available.
> > The reason is that there is no free bit left in the pmd entry for the
> > software dirty bit.
>
> Thanks for the quick reply.
>
> >
> > > So, if there isn't any help from s390, I should introduce
> > > HAVE_ARCH_THP_MADVFREE to disable MADV_FREE support of THP in s390 but
> > > not want to introduce such new config.
> >
> > Why is the dirty bit for pmds needed for the MADV_FREE implementation?
>
> MADV_FREE semantic want it.
>
> When madvise syscall is called, VM clears dirty bit of ptes of
> the range. If memory pressure happens, VM checks dirty bit of
> page table and if it found still "clean", it means it's a
> "lazyfree pages" so VM could discard the page instead of swapping out.
> Once there was store operation for the page before VM peek a page
> to reclaim, dirty bit is set so VM can swap out the page instead of
> discarding to keep up-to-date contents.
>
> If it's hard on s390, maybe we could use just reference bit
> instead of dirty bit to check recent access but it might change
> semantic a bit with other OSes. :(
Just discussed this with Gerald and we found a trick how we can add
a dirty bit to the pmd entries. That will be a non-trivial patch but
we can do it. Until that time you could just define pmd_dirty to
always return true and the code should "work" in the sense that it
does not break anything.
--
blue skies,
Martin.
"Reality continues to ruin my life." - Calvin.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH 08/10] userfaultfd: add new syscall to provide memory externalization
From: Andrea Arcangeli @ 2014-07-03 13:19 UTC (permalink / raw)
To: Andy Lutomirski
Cc: qemu-devel, kvm, linux-mm, linux-kernel,
"Dr. David Alan Gilbert", Johannes Weiner, Andrew Morton,
Android Kernel Team, Robert Love, Mel Gorman, Hugh Dickins,
Dave Hansen, Rik van Riel, Dmitry Adamushko, Neil Brown,
Mike Hommey, Taras Glek, Jan Kara, KOSAKI Motohiro,
Michel Lespinasse, Minchan Kim, Keith Packard,
"Huangpeng (Peter)" <peter.huang>
In-Reply-To: <53B4B833.9010508@mit.edu>
Hi Andy,
thanks for CC'ing linux-api.
On Wed, Jul 02, 2014 at 06:56:03PM -0700, Andy Lutomirski wrote:
> On 07/02/2014 09:50 AM, Andrea Arcangeli wrote:
> > Once an userfaultfd is created MADV_USERFAULT regions talks through
> > the userfaultfd protocol with the thread responsible for doing the
> > memory externalization of the process.
> >
> > The protocol starts by userland writing the requested/preferred
> > USERFAULT_PROTOCOL version into the userfault fd (64bit write), if
> > kernel knows it, it will ack it by allowing userland to read 64bit
> > from the userfault fd that will contain the same 64bit
> > USERFAULT_PROTOCOL version that userland asked. Otherwise userfault
> > will read __u64 value -1ULL (aka USERFAULTFD_UNKNOWN_PROTOCOL) and it
> > will have to try again by writing an older protocol version if
> > suitable for its usage too, and read it back again until it stops
> > reading -1ULL. After that the userfaultfd protocol starts.
> >
> > The protocol consists in the userfault fd reads 64bit in size
> > providing userland the fault addresses. After a userfault address has
> > been read and the fault is resolved by userland, the application must
> > write back 128bits in the form of [ start, end ] range (64bit each)
> > that will tell the kernel such a range has been mapped. Multiple read
> > userfaults can be resolved in a single range write. poll() can be used
> > to know when there are new userfaults to read (POLLIN) and when there
> > are threads waiting a wakeup through a range write (POLLOUT).
> >
> > Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
>
> > +#ifdef CONFIG_PROC_FS
> > +static int userfaultfd_show_fdinfo(struct seq_file *m, struct file *f)
> > +{
> > + struct userfaultfd_ctx *ctx = f->private_data;
> > + int ret;
> > + wait_queue_t *wq;
> > + struct userfaultfd_wait_queue *uwq;
> > + unsigned long pending = 0, total = 0;
> > +
> > + spin_lock(&ctx->fault_wqh.lock);
> > + list_for_each_entry(wq, &ctx->fault_wqh.task_list, task_list) {
> > + uwq = container_of(wq, struct userfaultfd_wait_queue, wq);
> > + if (uwq->pending)
> > + pending++;
> > + total++;
> > + }
> > + spin_unlock(&ctx->fault_wqh.lock);
> > +
> > + ret = seq_printf(m, "pending:\t%lu\ntotal:\t%lu\n", pending, total);
>
> This should show the protocol version, too.
Ok, does the below look ok?
diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
index 388553e..f9d3e9f 100644
--- a/fs/userfaultfd.c
+++ b/fs/userfaultfd.c
@@ -493,7 +493,13 @@ static int userfaultfd_show_fdinfo(struct seq_file *m, struct file *f)
}
spin_unlock(&ctx->fault_wqh.lock);
- ret = seq_printf(m, "pending:\t%lu\ntotal:\t%lu\n", pending, total);
+ /*
+ * If more protocols will be added, there will be all shown
+ * separated by a space. Like this:
+ * protocols: 0xaa 0xbb
+ */
+ ret = seq_printf(m, "pending:\t%lu\ntotal:\t%lu\nprotocols:\t%Lx\n",
+ pending, total, USERFAULTFD_PROTOCOL);
return ret;
}
> > +
> > +SYSCALL_DEFINE1(userfaultfd, int, flags)
> > +{
> > + int fd, error;
> > + struct file *file;
>
> This looks like it can't be used more than once in a process. That will
It can't be used more than once, correct.
file = ERR_PTR(-EBUSY);
if (get_mm_slot(current->mm))
goto out_free_unlock;
If a userfaultfd is already registered for the current mm the second
one gets -EBUSY.
> be unfortunate for libraries. Would it be feasible to either have
So you envision two userfaultfd memory managers for the same process?
I assume each one would claim separate ranges of memory?
For that case the demultiplexing of userfaults can be entirely managed
by userland.
One libuserfault library can actually register the userfaultfd, and
then the two libs can register into libuserfault and claim their own
ranges. It could run the code of the two libs in the thread context
that waits on the userfaultfd with zero overhead, or message passing
across threads can be used to run both libs in parallel in their own
thread. The demultiplexing code wouldn't be CPU intensive. The
downside are two schedule event required if they want to run their lib
code in a separate thread. If we'd claim the two different ranges in
the kernel for two different userfaultfd, the kernel would be speaking
directly with each library thread. That'd be the only advantage if
they don't want to run in the context of the thread that waits on the
userfaultfd.
To increase SMP scalability in the future we could also add a
UFFD_LOAD_BALANCE to distribute userfaults to different userfaultfd,
that if used could relax the -EBUSY (but it wouldn't be two different
claimed ranges for two different libs).
If passing UFFD_LOAD_BALANCE to the current code sys_userfaultfd would
return -EINVAL. I haven't implemented it because I'm not sure if such
thing would ever be needed. Compared to distributing the userfaults in
userland to different threads that would only save two context
switches per event. I don't see a problem in adding this later if a
need emerges though.
I think the best model for two libs claiming different userfault
ranges, is to run the userfault code of each lib in the context of the
thread that waits on the userfaultfd, and if there's a need to scale
in SMP we'd add UFFD_LOAD_BALANCE so multiple threads can wait on
different userfaultfd and scale optimally in SMP without any need of
spurious context switches.
With the volatile pages current code, the SIGBUS event would also be
mm-wide and require demultiplexing inside the sigbus handler if two
different libs wants to claim different ranges. Furthermore the sigbus
would run in the context of the faulting thread so it would still need
to context switch to scale (with userfaultfd we let the current thread
continue by triggering a schedule in the guest, if FOLL_NOWAIT fails
and we spawn a kworker thread doing the async page fault, then the
kworker kernel thread stops in the userfault and the migration thread
waiting on the userfaultfd is woken up to resolve the userfault).
Programs like qemu are unlikely to ever need more than one
userfaultfd, so it wouldn't need to use the demultiplexing
library. Currently we don't feel a need for UFFD_LOAD_BALANCE either.
However I'd rather implement UFFD_LOAD_BALANCE now, than claiming
different ranges in kernel that would require to build a lookup
structure that lives on top of the vmas and it wouldn't be less
efficient to write such a thing in userland inside a libuserfaultfd
(that qemu would likely never need).
In short I see no benefit in claiming different ranges for the same mm
in the kernel API (that can be done equally efficient in userland),
but I could see a benefit in a load balancing feature to scale the
load to multiple userfaultfd if passing UFFD_LOAD_BALANCE (it could
also be done by default by just removing the -EBUSY failure and
without new flags, but it sounds safer to keep -EBUSY if
UFFD_LOAD_BALANCE is not passed to userfaultfd through the flags).
> userfaultfd claim a range of addresses or for a vma to be explicitly
> associated with a userfaultfd? (In the latter case, giant PROT_NONE
> MAP_NORESERVE mappings could be used.)
To claim ranges MADV_USERFAULT is used and the vmas are
mm-wide. Instead of creating another range lookup on top the vmas, I
used the vma itself to tell which ranges are claimed for userfaultfd
(or SIGBUS behavior if userfaultfd isn't open).
This API model with MADV_USERFAULT to claim the userfaultfd ranges is
want fundamentally prevents you to claim different ranges for two
different userfaultfd.
About PROT_NONE, note that if you set the mapping as MADV_USERFAULT
there's no point in setting PROT_NONE too, MAP_NORESERVE instead
should already work just fine and it's orthogonal.
PROT_NONE is kind of an alternative to userfaults without
MADV_USERFAULT. Problem is that PROT_NONE requires to split VMAs (and
take the mmap_sem for writing and mangle and allocate vmas), until you
actually run out of vmas and you get -ENOMEM and the app crashes. The
whole point of postcopy live migration is that it will work with
massively large guests so we cannot risk running out of vmas.
With MADV_USERFAULT you never mangle the vma and you work with a
single gigantic vma that never gets split. You don't need to mremap
over the PROT_NONE to handle the fault.
Even without userfaultfd and just with MADV_USERFAULT+remap_anon_pages
using SIGBUS is a much more efficient and more reliable alternative
than PROT_NONE.
With userfaultfd + remap_anon_pages things are even more efficient
because there are no signals involved at all, and syscalls won't
return weird errors to userland like it would happen with SIGBUS and
without userfaultfd. With userfaultfd the thread stopping in the
userfault won't even exit the kernel, it'll wait a wakeup within the
kernel. And remap_anon_pages that resolves the fault, just updates the
ptes or hugepmds without touching the vma (plus remap_anon_pages is
very strict so the chance of memory corruption going unnoticed is next
to nil, unlike mremap).
Other things that people should comment on is that currently you
cannot set MADV_USERFAULT on filebacked vmas, that sounds like an
issue for volatile pages that should be fixed. Currently -EINVAL is
returned if MADV_USERFAULT is run on non anonymous vmas. I don't think
it's a problem to add it. The do_linear_fault would just trigger an
userfault too. Then it's up to userland how it resolves it before
sending the wakeup to the stopped thread with userfaultfd_write. As
long as it doesn't fault in do_linear_fault again it'll just work.
How you solve the fault before acking it with userfaultfd_write
(remap_file_pages, remap_anon_pages, mremap, mmap, anything) is
entirely up to userland. The only faults the userfault tracks are the
pte_none/pmd_none kind (same as PROT_NONE). As long as you put
something in the pte/pmd and it's not none anymore you can
userfaultfd_write and it'll just work. Clearing VM_USERFAULT would
also work to resolve the fault of course (and then it'd pagein from
disk if it was filebacked or it'd map a zero page if it was an
anonymous vma) but clearing VM_USERFAULT (with MADV_NOUSERFAULT)
would split the vma so it's not recommended...
Thanks,
Andrea
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* Re: [RFC PATCH 00/11] Adding FreeBSD's Capsicum security framework (part 1)
From: Loganaden Velvindron @ 2014-07-03 10:01 UTC (permalink / raw)
To: Paolo Bonzini
Cc: David Drysdale, linux-security-module, linux-kernel,
Greg Kroah-Hartman, Alexander Viro, Meredydd Luff, Kees Cook,
James Morris, linux-api, qemu-devel
In-Reply-To: <53B51E81.4090700@redhat.com>
On Thu, Jul 3, 2014 at 1:12 PM, Paolo Bonzini <pbonzini@redhat.com> wrote:
> Il 30/06/2014 12:28, David Drysdale ha scritto:
>>
>> Hi all,
>>
>> The last couple of versions of FreeBSD (9.x/10.x) have included the
>> Capsicum security framework [1], which allows security-aware
>> applications to sandbox themselves in a very fine-grained way. For
>> example, OpenSSH now (>= 6.5) uses Capsicum in its FreeBSD version to
>> restrict sshd's credentials checking process, to reduce the chances of
>> credential leakage.
Aside from OpenSSH, I've also been working on implementing Capsicum,
in other userspace software.
>
>
> Hi David,
>
> we've had similar goals in QEMU. QEMU can be used as a virtual machine
> monitor from the command line, but it also has an API that lets a management
> tool drive QEMU via AF_UNIX sockets. Long term, we would like to have a
> restricted mode for QEMU where all file descriptors are obtained via
> SCM_RIGHTS or /dev/fd, and syscalls can be locked down.
>
> Currently we do use seccomp v2 BPF filters, but unfortunately this didn't
> help very much. QEMU supports hotplugging hence the filter must whitelist
> anything that _might_ be used in the future, which is generally... too much.
>
> Something like Capsicum would be really nice because it attaches
> capabilities to file descriptors. However, I wonder however how extensible
> Capsicum could be, and I am worried about the proliferation of capabilities
> that its design naturally leads to.
>
> Given Linux's previous experience with BPF filters, what do you think about
> attaching specific BPF programs to file descriptors? Then whenever a
> syscall is run that affects a file descriptor, the BPF program for the file
> descriptor (attached to a struct file* as in Capsicum) would run in addition
> to the process-wide filter.
>
> An equivalent of PR_SET_NO_NEW_PRIVS can also be added to file descriptors,
> so that a program that doesn't lock down syscalls can still lock down the
> operations (including fcntls and ioctls) on specific file descriptors.
>
> Converting FreeBSD capabilities to BPF programs can be easily implemented in
> userspace.
>
>> [Capsicum also includes 'capability mode', which locks down the
>> available syscalls so the rights restrictions can't just be bypassed
>> by opening new file descriptors; I'll describe that separately later.]
>
>
> This can also be implemented in userspace via seccomp and
> PR_SET_NO_NEW_PRIVS.
>
>> [Policing the rights checks anywhere else, for example at the system
>> call boundary, isn't a good idea because it opens up the possibility
>> of time-of-check/time-of-use (TOCTOU) attacks [2] where FDs are
>> changed (as openat/close/dup2 are allowed in capability mode) between
>> the 'check' at syscall entry and the 'use' at fget() invocation.]
>
>
> In the case of BPF filters, I wonder if you could stash the BPF
> "environment" somewhere and then use it at fget() invocation. Alternatively,
> it can be reconstructed at fget() time, similar to your introduction of
> fgetr().
>
> Thanks,
>
> Paolo
> --
> To unsubscribe from this list: send the line "unsubscribe
> linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
This message is strictly personal and the opinions expressed do not
represent those of my employers, either past or present.
^ permalink raw reply
* RE: [PATCH RFC net-next 08/14] bpf: add eBPF verifier
From: David Laight @ 2014-07-03 9:13 UTC (permalink / raw)
To: 'Alexei Starovoitov', Chema Gonzalez
Cc: David S. Miller, Ingo Molnar, Linus Torvalds, Steven Rostedt,
Daniel Borkmann, Eric Dumazet, Peter Zijlstra,
Arnaldo Carvalho de Melo, Jiri Olsa, Thomas Gleixner,
H. Peter Anvin, Andrew Morton, Kees Cook, Linux API,
Network Development, LKML
In-Reply-To: <CAMEtUuxfrAUDDQVGNsNV2PeDii-6LAwQCbB2XGEHTJDwkkvyWg@mail.gmail.com>
From: Alexei Starovoitov
> >> +#define _(OP) ({ int ret = OP; if (ret < 0) return ret; })
> > +1 to removing the _ macro. If you want to avoid the 3 lines (is there
> > anything in the style guide against "if ((err=OP) < 0) ..." ?), at
>
> assignment and function call inside 'if' ? I don't like such style.
>
> > least use some meaningful macro name (DO_AND_CHECK, or something like
> > that).
It would have to be RETURN_IF_NEGATIVE().
But even then it is skipped by searches for 'return'.
> Try replacing _ with any other name and see how bad it will look.
> I tried with MACRO_NAME and with 'if (err) goto' and with 'if (err) return',
> before I converged on _ macro.
> I think it's a hidden gem of this patch.
No, it is one of those things that 'seems like a good idea at the time',
but causes grief much later on.
Have you considered saving the error code into 'env' and making most of
the functions return if an error is set?
Then the calling code need not check the result of every function call.
David
^ permalink raw reply
* Re: [RFC PATCH 00/11] Adding FreeBSD's Capsicum security framework (part 1)
From: Paolo Bonzini @ 2014-07-03 9:12 UTC (permalink / raw)
To: David Drysdale, linux-security-module, linux-kernel,
Greg Kroah-Hartman
Cc: Alexander Viro, Meredydd Luff, Kees Cook, James Morris, linux-api,
qemu-devel
In-Reply-To: <1404124096-21445-1-git-send-email-drysdale@google.com>
Il 30/06/2014 12:28, David Drysdale ha scritto:
> Hi all,
>
> The last couple of versions of FreeBSD (9.x/10.x) have included the
> Capsicum security framework [1], which allows security-aware
> applications to sandbox themselves in a very fine-grained way. For
> example, OpenSSH now (>= 6.5) uses Capsicum in its FreeBSD version to
> restrict sshd's credentials checking process, to reduce the chances of
> credential leakage.
Hi David,
we've had similar goals in QEMU. QEMU can be used as a virtual machine
monitor from the command line, but it also has an API that lets a
management tool drive QEMU via AF_UNIX sockets. Long term, we would
like to have a restricted mode for QEMU where all file descriptors are
obtained via SCM_RIGHTS or /dev/fd, and syscalls can be locked down.
Currently we do use seccomp v2 BPF filters, but unfortunately this
didn't help very much. QEMU supports hotplugging hence the filter must
whitelist anything that _might_ be used in the future, which is
generally... too much.
Something like Capsicum would be really nice because it attaches
capabilities to file descriptors. However, I wonder however how
extensible Capsicum could be, and I am worried about the proliferation
of capabilities that its design naturally leads to.
Given Linux's previous experience with BPF filters, what do you think
about attaching specific BPF programs to file descriptors? Then
whenever a syscall is run that affects a file descriptor, the BPF
program for the file descriptor (attached to a struct file* as in
Capsicum) would run in addition to the process-wide filter.
An equivalent of PR_SET_NO_NEW_PRIVS can also be added to file
descriptors, so that a program that doesn't lock down syscalls can still
lock down the operations (including fcntls and ioctls) on specific file
descriptors.
Converting FreeBSD capabilities to BPF programs can be easily
implemented in userspace.
> [Capsicum also includes 'capability mode', which locks down the
> available syscalls so the rights restrictions can't just be bypassed
> by opening new file descriptors; I'll describe that separately later.]
This can also be implemented in userspace via seccomp and
PR_SET_NO_NEW_PRIVS.
> [Policing the rights checks anywhere else, for example at the system
> call boundary, isn't a good idea because it opens up the possibility
> of time-of-check/time-of-use (TOCTOU) attacks [2] where FDs are
> changed (as openat/close/dup2 are allowed in capability mode) between
> the 'check' at syscall entry and the 'use' at fget() invocation.]
In the case of BPF filters, I wonder if you could stash the BPF
"environment" somewhere and then use it at fget() invocation.
Alternatively, it can be reconstructed at fget() time, similar to your
introduction of fgetr().
Thanks,
Paolo
^ permalink raw reply
* Re: [PATCH v9] mm: support madvise(MADV_FREE)
From: Minchan Kim @ 2014-07-03 8:37 UTC (permalink / raw)
To: Martin Schwidefsky
Cc: Kirill A. Shutemov, Andrew Morton, linux-kernel, linux-mm,
Michael Kerrisk, Linux API, Hugh Dickins, Johannes Weiner,
Rik van Riel, KOSAKI Motohiro, Mel Gorman, Jason Evans,
Zhang Yanfei, Heiko Carstens, linux390, Gerald Schaefer
In-Reply-To: <20140703102901.322bfdb0@mschwide>
Hello,
On Thu, Jul 03, 2014 at 10:29:01AM +0200, Martin Schwidefsky wrote:
> On Thu, 3 Jul 2014 16:29:54 +0900
> Minchan Kim <minchan@kernel.org> wrote:
>
> > Hello,
> >
> > On Thu, Jul 03, 2014 at 10:03:19AM +0900, Minchan Kim wrote:
> > > Hello,
> > >
> > > On Tue, Jul 01, 2014 at 05:50:58PM +0300, Kirill A. Shutemov wrote:
> > > > On Tue, Jul 01, 2014 at 09:36:15AM +0900, Minchan Kim wrote:
> > > > > + do {
> > > > > + /*
> > > > > + * XXX: We can optimize with supporting Hugepage free
> > > > > + * if the range covers.
> > > > > + */
> > > > > + next = pmd_addr_end(addr, end);
> > > > > + if (pmd_trans_huge(*pmd))
> > > > > + split_huge_page_pmd(vma, addr, pmd);
> > > >
> > > > Could you implement proper THP support before upstreaming the feature?
> > > > It shouldn't be a big deal.
> > >
> > > Okay, Hope to review.
> > >
> > > Thanks for the feedback!
> > >
> >
> > I tried to implement it but had a issue.
> >
> > I need pmd_mkold, pmd_mkclean for MADV_FREE operation and pmd_dirty for
> > page_referenced. When I investigate all of arches supported THP,
> > it's not a big deal but s390 is not sure to me who has no idea of
> > soft tracking of s390 by storage key instead of page table information.
> > Cced s390 maintainer. Hope to help.
>
> Storage key for dirty and referenced tracking is a thing of the past.
> The current code for s390 uses software tracking for dirty and referenced.
> There is one catch though, for ptes the software implementation covers
> dirty and referenced bit but for pmds only referenced bit is available.
> The reason is that there is no free bit left in the pmd entry for the
> software dirty bit.
Thanks for the quick reply.
>
> > So, if there isn't any help from s390, I should introduce
> > HAVE_ARCH_THP_MADVFREE to disable MADV_FREE support of THP in s390 but
> > not want to introduce such new config.
>
> Why is the dirty bit for pmds needed for the MADV_FREE implementation?
MADV_FREE semantic want it.
When madvise syscall is called, VM clears dirty bit of ptes of
the range. If memory pressure happens, VM checks dirty bit of
page table and if it found still "clean", it means it's a
"lazyfree pages" so VM could discard the page instead of swapping out.
Once there was store operation for the page before VM peek a page
to reclaim, dirty bit is set so VM can swap out the page instead of
discarding to keep up-to-date contents.
If it's hard on s390, maybe we could use just reference bit
instead of dirty bit to check recent access but it might change
semantic a bit with other OSes. :(
>
> --
> blue skies,
> Martin.
>
> "Reality continues to ruin my life." - Calvin.
>
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo@kvack.org. For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
--
Kind regards,
Minchan Kim
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH v9] mm: support madvise(MADV_FREE)
From: Martin Schwidefsky @ 2014-07-03 8:29 UTC (permalink / raw)
To: Minchan Kim
Cc: Kirill A. Shutemov, Andrew Morton, linux-kernel, linux-mm,
Michael Kerrisk, Linux API, Hugh Dickins, Johannes Weiner,
Rik van Riel, KOSAKI Motohiro, Mel Gorman, Jason Evans,
Zhang Yanfei, Heiko Carstens, linux390, Gerald Schaefer
In-Reply-To: <20140703072954.GC2939@bbox>
On Thu, 3 Jul 2014 16:29:54 +0900
Minchan Kim <minchan@kernel.org> wrote:
> Hello,
>
> On Thu, Jul 03, 2014 at 10:03:19AM +0900, Minchan Kim wrote:
> > Hello,
> >
> > On Tue, Jul 01, 2014 at 05:50:58PM +0300, Kirill A. Shutemov wrote:
> > > On Tue, Jul 01, 2014 at 09:36:15AM +0900, Minchan Kim wrote:
> > > > + do {
> > > > + /*
> > > > + * XXX: We can optimize with supporting Hugepage free
> > > > + * if the range covers.
> > > > + */
> > > > + next = pmd_addr_end(addr, end);
> > > > + if (pmd_trans_huge(*pmd))
> > > > + split_huge_page_pmd(vma, addr, pmd);
> > >
> > > Could you implement proper THP support before upstreaming the feature?
> > > It shouldn't be a big deal.
> >
> > Okay, Hope to review.
> >
> > Thanks for the feedback!
> >
>
> I tried to implement it but had a issue.
>
> I need pmd_mkold, pmd_mkclean for MADV_FREE operation and pmd_dirty for
> page_referenced. When I investigate all of arches supported THP,
> it's not a big deal but s390 is not sure to me who has no idea of
> soft tracking of s390 by storage key instead of page table information.
> Cced s390 maintainer. Hope to help.
Storage key for dirty and referenced tracking is a thing of the past.
The current code for s390 uses software tracking for dirty and referenced.
There is one catch though, for ptes the software implementation covers
dirty and referenced bit but for pmds only referenced bit is available.
The reason is that there is no free bit left in the pmd entry for the
software dirty bit.
> So, if there isn't any help from s390, I should introduce
> HAVE_ARCH_THP_MADVFREE to disable MADV_FREE support of THP in s390 but
> not want to introduce such new config.
Why is the dirty bit for pmds needed for the MADV_FREE implementation?
--
blue skies,
Martin.
"Reality continues to ruin my life." - Calvin.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH v9] mm: support madvise(MADV_FREE)
From: Minchan Kim @ 2014-07-03 7:29 UTC (permalink / raw)
To: Kirill A. Shutemov
Cc: Andrew Morton, linux-kernel, linux-mm, Michael Kerrisk, Linux API,
Hugh Dickins, Johannes Weiner, Rik van Riel, KOSAKI Motohiro,
Mel Gorman, Jason Evans, Zhang Yanfei, Martin Schwidefsky,
Heiko Carstens, linux390, Gerald Schaefer
In-Reply-To: <20140703010318.GA2939@bbox>
Hello,
On Thu, Jul 03, 2014 at 10:03:19AM +0900, Minchan Kim wrote:
> Hello,
>
> On Tue, Jul 01, 2014 at 05:50:58PM +0300, Kirill A. Shutemov wrote:
> > On Tue, Jul 01, 2014 at 09:36:15AM +0900, Minchan Kim wrote:
> > > + do {
> > > + /*
> > > + * XXX: We can optimize with supporting Hugepage free
> > > + * if the range covers.
> > > + */
> > > + next = pmd_addr_end(addr, end);
> > > + if (pmd_trans_huge(*pmd))
> > > + split_huge_page_pmd(vma, addr, pmd);
> >
> > Could you implement proper THP support before upstreaming the feature?
> > It shouldn't be a big deal.
>
> Okay, Hope to review.
>
> Thanks for the feedback!
>
I tried to implement it but had a issue.
I need pmd_mkold, pmd_mkclean for MADV_FREE operation and pmd_dirty for
page_referenced. When I investigate all of arches supported THP,
it's not a big deal but s390 is not sure to me who has no idea of
soft tracking of s390 by storage key instead of page table information.
Cced s390 maintainer. Hope to help.
So, if there isn't any help from s390, I should introduce
HAVE_ARCH_THP_MADVFREE to disable MADV_FREE support of THP in s390 but
not want to introduce such new config.
At least, jemalloc case, it's hard to play with THP because it has
some metadata in the head of chunk so normally it doesn't free 2M
entirely. I guess other allocator works with similar approach
so not sure it's worth in this stage.
Do you have any workload to use MADV_FREE with THP?
so do you really want to support THP MADV_FREE now?
--
Kind regards,
Minchan Kim
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox