diff -urN zabbix-2.2.1.orig/frontends/php/include/classes/validators/CTriggerFunctionValidator.php zabbix-2.2.1/frontends/php/include/classes/validators/CTriggerFunctionValidator.php
--- zabbix-2.2.1.orig/frontends/php/include/classes/validators/CTriggerFunctionValidator.php	2014-01-13 08:29:57.505651636 -0800
+++ zabbix-2.2.1/frontends/php/include/classes/validators/CTriggerFunctionValidator.php	2014-01-13 08:42:02.381312201 -0800
@@ -175,6 +175,14 @@
 				),
 				'value_types' => $valueTypesNum
 			),
+			'percentile' => array(
+				'args' => array(
+					array('type' => 'sec_num', 'mandat' => true),
+					array('type' => 'num', 'mandat' => true),
+					array('type' => 'sec_zero', 'can_be_empty' => true)
+				),
+				'value_types' => $valueTypesNum
+			),
 			'nodata'=> array(
 				'args' => array(
 					array('type' => 'sec_zero', 'mandat' => true)
diff -urN zabbix-2.2.1.orig/frontends/php/include/triggers.inc.php zabbix-2.2.1/frontends/php/include/triggers.inc.php
--- zabbix-2.2.1.orig/frontends/php/include/triggers.inc.php	2014-01-13 08:29:57.515651575 -0800
+++ zabbix-2.2.1/frontends/php/include/triggers.inc.php	2014-01-13 08:46:28.529728215 -0800
@@ -2229,6 +2229,7 @@
 		'logsource' =>	array('value_type' => _('0 or 1'),	'type' => T_ZBX_INT,			'validation' => IN('0,1')),
 		'max' =>		array('value_type' => $value_type,	'type' => $type_of_value_type,	'validation' => NOT_EMPTY),
 		'min' =>		array('value_type' => $value_type,	'type' => $type_of_value_type,	'validation' => NOT_EMPTY),
+		'percentile' =>	array('value_type' => $value_type,	'type' => $type_of_value_type,	'validation' => NOT_EMPTY),
 		'nodata' =>		array('value_type' => _('0 or 1'),	'type' => T_ZBX_INT,			'validation' => IN('0,1')),
 		'now' =>		array('value_type' => _('Numeric (integer 64bit)'), 'type' => T_ZBX_INT, 'validation' => NOT_EMPTY),
 		'prev' =>		array('value_type' => $value_type,	'type' => $type_of_value_type,	'validation' => NOT_EMPTY),
diff -urN zabbix-2.2.1.orig/frontends/php/popup_trexpr.php zabbix-2.2.1/frontends/php/popup_trexpr.php
--- zabbix-2.2.1.orig/frontends/php/popup_trexpr.php	2014-01-13 08:29:57.415652177 -0800
+++ zabbix-2.2.1/frontends/php/popup_trexpr.php	2014-01-13 09:00:19.524777811 -0800
@@ -77,6 +77,21 @@
 		'M' => $metrics // metrcis
 	)
 );
+$param2PcntSecCount = array(
+	array(
+		'C' => _('Last of').' (T)',	/* caption */
+		'T' => T_ZBX_INT,	/* type */
+		'M' => $metrics		/* metrcis */
+	),
+		array(
+		'C' => 'Percentile (P)', /* caption */
+		'T' => T_ZBX_STR /* I would use T_ZBX_INT but then we get "seconds" text after the parameter box. */
+	),
+	array(
+		'C' => _('Time shift').' ', // caption
+		'T' => T_ZBX_INT // type
+	)
+);
 $param3SecVal = array(
 	array(
 		'C' => _('Last of').' (T)', // caption
@@ -295,6 +310,26 @@
 		'params' => $param1SecCount,
 		'allowed_types' => $allowedTypesNumeric
 		),
+	'percentile[<]' => array(
+		'description' =>  _('Pth percentile for period T is < N'),
+		'params' => $param2PcntSecCount,
+		'allowed_types' => $allowedTypesNumeric
+		),
+	'percentile[>]' => array(
+		'description' =>  _('Pth percentile for period T is > N'),
+		'params' => $param2PcntSecCount,
+		'allowed_types' => $allowedTypesNumeric
+		),
+	'percentile[=]' => array(
+		'description' =>  _('Pth percentile for period T is = N'),
+		'params' => $param2PcntSecCount,
+		'allowed_types' => $allowedTypesNumeric
+		),
+	'percentile[#]' => array(
+		'description' =>  _('Pth percentile for period T is NOT N'),
+		'params' => $param2PcntSecCount,
+		'allowed_types' => $allowedTypesNumeric
+		),
 	'prev[<]' => array(
 		'description' =>  _('Previous value is < N'),
 		'allowed_types' => $allowedTypesAny
diff -urN zabbix-2.2.1.orig/src/libs/zbxserver/evalfunc.c zabbix-2.2.1/src/libs/zbxserver/evalfunc.c
--- zabbix-2.2.1.orig/src/libs/zbxserver/evalfunc.c	2014-01-13 08:29:57.615650979 -0800
+++ zabbix-2.2.1/src/libs/zbxserver/evalfunc.c	2014-01-13 12:16:16.595717765 -0800
@@ -30,6 +30,16 @@
 	return fabs(a - b) < TRIGGER_EPSILON ? SUCCEED : FAIL;
 }
 
+int	compare_float(const void *float1, const void *float2) {
+	if (*(double *)float1 > *(double *)float2)
+		return 1;
+	else if (*(double *)float1 < *(double *)float2)
+		return -1;
+	else
+		return 0;
+
+}
+
 static int	__get_function_parameter_uint31(zbx_uint64_t hostid, const char *parameters, int Nparam,
 		int *value, int *flag, int defaults_on_empty, int def_value, int def_flag)
 {
@@ -1009,6 +1019,141 @@
 
 /******************************************************************************
  *                                                                            *
+ * Function: evaluate_PERCENTILE                                              *
+ *                                                                            *
+ * Purpose: evaluate function 'percentile' for the item                       *
+ *                                                                            *
+ * Parameters: item - item (performance metric)                               *
+ *             parameters - percent and number of seconds/values              *
+ *                                                                            *
+ * Return value: SUCCEED - evaluated successfully, result is stored in 'value'*
+ *               FAIL - failed to evaluate function                           *
+ *                                                                            *
+ * Author: Matthew Gould                                                      *
+ *         Based on patch for Zabbix 1.8 by Sergey Kononenko                  *
+ *                                                                            *
+ * Comments:                                                                  *
+ *                                                                            *
+ ******************************************************************************/
+static int      evaluate_PERCENTILE(char *value, DB_ITEM *item, const char *function, const char *parameters, time_t now)
+{
+	const char      *__function_name = "evaluate_PERCENTILE";
+	int             nparams, arg1, percent, percent_index, seconds = 0, nvalues = 0, flag, percent_flag, rows = 0, ret = FAIL;
+
+	zbx_vector_history_record_t     values;
+
+	zbx_uint64_t    *auint64 = NULL;
+	double          *afloat = NULL;
+
+	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
+
+	zbx_history_record_vector_create(&values);
+
+	if (ITEM_VALUE_TYPE_FLOAT != item->value_type && ITEM_VALUE_TYPE_UINT64 != item->value_type)
+		goto out;
+
+	nparams = num_param(parameters);
+	if (!(nparams == 2 || nparams == 3))
+		goto out;
+
+	if (SUCCEED != get_function_parameter_uint31(item->hostid, parameters, 1, &arg1, &flag) || 0 == arg1)
+		goto out;
+
+	if (SUCCEED != get_function_parameter_uint31(item->hostid, parameters, 2, &percent, &percent_flag) || 0 == percent)
+		goto out;
+
+	if (nparams == 3)
+	{
+		int time_shift, time_shift_flag;
+
+		if (FAIL == get_function_parameter_uint31_default(item->hostid, parameters, 3, &time_shift,
+					&time_shift_flag, 0, ZBX_FLAG_SEC) || ZBX_FLAG_SEC != time_shift_flag)
+			goto out;
+
+		now -= time_shift;
+	}
+
+	if (percent < 0 || percent > 100)
+	{
+		zabbix_log(LOG_LEVEL_WARNING, "No result for invalid %d PERCENTILE", percent);
+		goto out;
+	}
+	if (ZBX_FLAG_SEC == flag)
+		seconds = arg1;
+	else
+		nvalues = arg1;
+
+	if (FAIL == zbx_vc_get_value_range(item->itemid, item->value_type, &values, seconds, nvalues, now))
+		goto out;
+
+
+	if (0 < values.values_num)
+	{
+
+		if (item->value_type == ITEM_VALUE_TYPE_UINT64)
+		{
+			auint64 = zbx_malloc(auint64, sizeof(zbx_uint64_t) * values.values_num);
+
+			for (rows = 0; rows < values.values_num; rows++)
+			{
+				auint64[rows] = values.values[rows].value.ui64;
+			}
+		}
+		else
+		{
+			afloat = zbx_malloc(afloat, sizeof(double) * values.values_num);
+
+			for (rows = 0; rows < values.values_num; rows++)
+			{
+				afloat[rows] = values.values[rows].value.dbl;
+			}
+		}
+
+		if (100 == percent)
+		{
+			percent_index = values.values_num-1;
+		}
+		else
+		{
+			percent_index = (int)((percent/100.0)*values.values_num);
+		}
+
+		if (ITEM_VALUE_TYPE_UINT64 == item->value_type)
+		{
+			qsort(auint64, values.values_num, sizeof(zbx_uint64_t), zbx_default_uint64_compare_func);
+			zbx_snprintf(value, MAX_BUFFER_LEN, ZBX_FS_UI64, auint64[percent_index]);
+		}
+		else
+		{
+			qsort(afloat, values.values_num, sizeof(double), compare_float);
+			zbx_snprintf(value, MAX_BUFFER_LEN, ZBX_FS_DBL, afloat[percent_index]);
+		}
+		ret = SUCCEED;
+
+		if (item->value_type == ITEM_VALUE_TYPE_UINT64)
+		{
+			zbx_free(auint64);
+		}
+		else
+		{
+			zbx_free(afloat);
+		}
+
+	}
+	else
+		zabbix_log(LOG_LEVEL_DEBUG, "result for PERCENTILE is empty");
+
+
+out:
+	zbx_history_record_vector_destroy(&values, item->value_type);
+
+	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
+
+	return ret;
+}
+
+/******************************************************************************
+ *                                                                            *
  * Function: evaluate_DELTA                                                   *
  *                                                                            *
  * Purpose: evaluate function 'delta' for the item                            *
@@ -1725,6 +1870,10 @@
 	{
 		ret = evaluate_MAX(value, item, function, parameter, now);
 	}
+	else if (0 == strcmp(function, "percentile"))
+	{
+		ret = evaluate_PERCENTILE(value, item, function, parameter, now);
+	}
 	else if (0 == strcmp(function, "avg"))
 	{
 		ret = evaluate_AVG(value, item, function, parameter, now);
diff -urN zabbix-2.2.1.orig/src/libs/zbxserver/evalfunc.h zabbix-2.2.1/src/libs/zbxserver/evalfunc.h
--- zabbix-2.2.1.orig/src/libs/zbxserver/evalfunc.h	2014-01-13 08:29:57.615650979 -0800
+++ zabbix-2.2.1/src/libs/zbxserver/evalfunc.h	2014-01-13 12:16:47.265539330 -0800
@@ -27,6 +27,7 @@
 #define ZBX_FLAG_VALUES	1
 
 int	cmp_double(double a, double b);
+int	compare_float(const void *float1, const void *float2);
 
 int	evaluate_macro_function(char *value, const char *host, const char *key,
 		const char *function, const char *parameter);
