Compare commits

...

3 Commits

Author SHA1 Message Date
CentOS Sources
4e376fae23 import trace-cmd-2.7-10.el8 2022-05-10 11:59:33 +00:00
CentOS Sources
03f3734d18 import trace-cmd-2.7-9.el8 2021-09-10 05:20:51 +00:00
CentOS Sources
5207fcaa56 import trace-cmd-2.7-8.el8 2021-09-10 05:20:48 +00:00
5 changed files with 550 additions and 10 deletions

View File

@ -0,0 +1,65 @@
From 8e8f12df7500f501447a2926a9e1db9ff42d882c Mon Sep 17 00:00:00 2001
From: Rikard Falkeborn <rikard.falkeborn@gmail.com>
Date: Fri, 12 Apr 2019 11:22:49 -0300
Subject: [PATCH] tools lib traceevent: Fix missing equality check for strcmp
There was a missing comparison with 0 when checking if type is "s64" or
"u64". Therefore, the body of the if-statement was entered if "type" was
"u64" or not "s64", which made the first strcmp() redundant since if
type is "u64", it's not "s64".
If type is "s64", the body of the if-statement is not entered but since
the remainder of the function consists of if-statements which will not
be entered if type is "s64", we will just return "val", which is
correct, albeit at the cost of a few more calls to strcmp(), i.e., it
will behave just as if the if-statement was entered.
If type is neither "s64" or "u64", the body of the if-statement will be
entered incorrectly and "val" returned. This means that any type that is
checked after "s64" and "u64" is handled the same way as "s64" and
"u64", i.e., the limiting of "val" to fit in for example "s8" is never
reached.
This was introduced in the kernel tree when the sources were copied from
trace-cmd in commit f7d82350e597 ("tools/events: Add files to create
libtraceevent.a"), and in the trace-cmd repo in 1cdbae6035cei
("Implement typecasting in parser") when the function was introduced,
i.e., it has always behaved the wrong way.
Detected by cppcheck.
Link: http://lkml.kernel.org/r/20190412142250.20595-7-acme@kernel.org
Signed-off-by: Rikard Falkeborn <rikard.falkeborn@gmail.com>
Reviewed-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Clark Williams <williams@redhat.com>
Cc: Tzvetomir Stoyanov <tstoyanov@vmware.com>
Fixes: f7d82350e597 ("tools/events: Add files to create libtraceevent.a")
Link: http://lkml.kernel.org/r/20190409091529.2686-1-rikard.falkeborn@gmail.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: John Kacur <jkacur@redhat.com>
---
event-parse.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/event-parse.c b/event-parse.c
index e5f2acbb70cc..7dba02bd19fe 100644
--- a/event-parse.c
+++ b/event-parse.c
@@ -2206,7 +2206,7 @@ eval_type_str(unsigned long long val, const char *type, int pointer)
return val & 0xffffffff;
if (strcmp(type, "u64") == 0 ||
- strcmp(type, "s64"))
+ strcmp(type, "s64") == 0)
return val;
if (strcmp(type, "s8") == 0)
--
2.20.1

View File

@ -0,0 +1,105 @@
From c4936880059251b28b7049f01bbfddb1882bda0e Mon Sep 17 00:00:00 2001
From: Slavomir Kaslev <kaslevs@vmware.com>
Date: Wed, 17 Apr 2019 21:31:57 +0300
Subject: [PATCH 2/2] trace-cmd: Add --no-filter option to not filter out
recording processes
Add trace-cmd --no-filter option that disables filtering out of trace-cmd
recording processes from the trace.
Link: http://lore.kernel.org/linux-trace-devel/20190417183157.8776-2-kaslevs@vmware.com
Tested-by: Phil Auld <pauld@redhat.com>
Suggested-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: Slavomir Kaslev <kaslevs@vmware.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
Documentation/trace-cmd-record.1.txt | 5 +++++
trace-record.c | 14 +++++++++++---
trace-usage.c | 1 +
3 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/Documentation/trace-cmd-record.1.txt b/Documentation/trace-cmd-record.1.txt
index 68afa161ac32..26a8299cd860 100644
--- a/Documentation/trace-cmd-record.1.txt
+++ b/Documentation/trace-cmd-record.1.txt
@@ -83,6 +83,11 @@ OPTIONS
The above are usually safe to use to compare fields.
+*--no-filter*::
+ Do not filter out the trace-cmd threads. By default, the threads are
+ filtered out to not be traced by events. This option will have the trace-cmd
+ threads also be traced.
+
*-R* 'trigger'::
Specify a trigger for the previous event. This must come after a *-e*.
This will add a given trigger to the given event. To only enable the trigger
diff --git a/trace-record.c b/trace-record.c
index e636c97d701d..ef4c4c873fd4 100644
--- a/trace-record.c
+++ b/trace-record.c
@@ -99,6 +99,7 @@ static int do_ptrace;
static int filter_task;
static int filter_pid = -1;
+static bool no_filter = false;
static int local_cpu_count;
@@ -1018,6 +1019,9 @@ static void update_task_filter(void)
struct buffer_instance *instance;
int pid = getpid();
+ if (no_filter)
+ return;
+
if (filter_task)
add_filter_pid(pid, 0);
@@ -4343,9 +4347,9 @@ void update_first_instance(struct buffer_instance *instance, int topt)
}
enum {
-
- OPT_quiet = 246,
- OPT_debug = 247,
+ OPT_quiet = 245,
+ OPT_debug = 246,
+ OPT_no_filter = 247,
OPT_max_graph_depth = 248,
OPT_tsoffset = 249,
OPT_bycomm = 250,
@@ -4592,6 +4596,7 @@ static void parse_record_options(int argc,
{"by-comm", no_argument, NULL, OPT_bycomm},
{"ts-offset", required_argument, NULL, OPT_tsoffset},
{"max-graph-depth", required_argument, NULL, OPT_max_graph_depth},
+ {"no-filter", no_argument, NULL, OPT_no_filter},
{"debug", no_argument, NULL, OPT_debug},
{"quiet", no_argument, NULL, OPT_quiet},
{"help", no_argument, NULL, '?'},
@@ -4866,6 +4871,9 @@ static void parse_record_options(int argc,
if (!ctx->max_graph_depth)
die("Could not allocate option");
break;
+ case OPT_no_filter:
+ no_filter = true;
+ break;
case OPT_debug:
debug = 1;
break;
diff --git a/trace-usage.c b/trace-usage.c
index 10f737b340df..dc73d4d90688 100644
--- a/trace-usage.c
+++ b/trace-usage.c
@@ -56,6 +56,7 @@ static struct usage_help usage_help[] = {
" --func-stack perform a stack trace for function tracer\n"
" (use with caution)\n"
" --max-graph-depth limit function_graph depth\n"
+ " --no-filter include trace-cmd threads in the trace\n"
},
{
"start",
--
2.20.1

View File

@ -0,0 +1,174 @@
From b206acf0f4bb4a8aff22b14139b6d5fdcb110b77 Mon Sep 17 00:00:00 2001
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
Date: Wed, 2 Jun 2021 11:08:03 +0200
Subject: [PATCH] trace-cmd: Add option to poll trace buffers
Waiting for data to be available on the trace ring-buffers may trigger
IPIs. This might generate unacceptable trace noise when debugging low
latency or real time systems. So introduce the poll option. When
enabled, it forces trace-cmd to use O_NONBLOCK. The drawback to using it
is that traces will be extracted by busy waiting, which will
unnecessarily hog the CPUs, so only use when really needed.
Link: https://lore.kernel.org/linux-trace-devel/20210602090803.12233-1-nsaenzju@redhat.com
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
Documentation/trace-cmd-record.1.txt | 7 +++++++
trace-cmd.h | 4 +++-
trace-record.c | 9 ++++++++-
trace-recorder.c | 29 +++++++++++++++-------------
trace-usage.c | 1 +
5 files changed, 35 insertions(+), 15 deletions(-)
diff --git a/Documentation/trace-cmd-record.1.txt b/Documentation/trace-cmd-record.1.txt
index 26a8299c..2b3ce636 100644
--- a/Documentation/trace-cmd-record.1.txt
+++ b/Documentation/trace-cmd-record.1.txt
@@ -331,6 +331,13 @@ OPTIONS
executed will not be changed. This is useful if you want to monitor the
output of the command being executed, but not see the output from trace-cmd.
+*--poll*::
+ Waiting for data to be available on the trace ring-buffers may trigger
+ IPIs. This might generate unacceptable trace noise when tracing low latency
+ or real time systems. The poll option forces trace-cmd to use O_NONBLOCK.
+ Traces are extracted by busy waiting, which will hog the CPUs, so only use
+ when really needed.
+
EXAMPLES
--------
diff --git a/trace-cmd.h b/trace-cmd.h
index 75951b5e..4cc9db2a 100644
--- a/trace-cmd.h
+++ b/trace-cmd.h
@@ -279,7 +279,9 @@ int tracecmd_attach_cpu_data_fd(int fd, int cpus, char * const *cpu_data_files);
enum {
TRACECMD_RECORD_NOSPLICE = (1 << 0), /* Use read instead of splice */
TRACECMD_RECORD_SNAPSHOT = (1 << 1), /* extract from snapshot */
- TRACECMD_RECORD_BLOCK = (1 << 2), /* Block on splice write */
+ TRACECMD_RECORD_BLOCK_SPLICE = (1 << 2), /* Block on splice write */
+ TRACECMD_RECORD_NOBRASS = (1 << 3), /* Splice directly without a brass pipe */
+ TRACECMD_RECORD_POLL = (1 << 4), /* Use O_NONBLOCK, poll trace buffers */
};
void tracecmd_free_recorder(struct tracecmd_recorder *recorder);
diff --git a/trace-record.c b/trace-record.c
index ef4c4c87..1872e3c4 100644
--- a/trace-record.c
+++ b/trace-record.c
@@ -2622,7 +2622,7 @@ create_recorder_instance_pipe(struct buffer_instance *instance,
int cpu, int *brass)
{
struct tracecmd_recorder *recorder;
- unsigned flags = recorder_flags | TRACECMD_RECORD_BLOCK;
+ unsigned flags = recorder_flags | TRACECMD_RECORD_BLOCK_SPLICE;
char *path;
if (instance->name)
@@ -4359,6 +4359,9 @@ enum {
OPT_funcstack = 254,
OPT_date = 255,
OPT_module = 256,
+ OPT_nofifos = 257,
+ OPT_cmdlines_size = 258,
+ OPT_poll = 259,
};
void trace_stop(int argc, char **argv)
@@ -4601,6 +4604,7 @@ static void parse_record_options(int argc,
{"quiet", no_argument, NULL, OPT_quiet},
{"help", no_argument, NULL, '?'},
{"module", required_argument, NULL, OPT_module},
+ {"poll", no_argument, NULL, OPT_poll},
{NULL, 0, NULL, 0}
};
@@ -4884,6 +4888,9 @@ static void parse_record_options(int argc,
ctx->instance->filter_mod = optarg;
ctx->filtered = 0;
break;
+ case OPT_poll:
+ recorder_flags |= TRACECMD_RECORD_POLL;
+ break;
case OPT_quiet:
case 'q':
quiet = 1;
diff --git a/trace-recorder.c b/trace-recorder.c
index 75290285..97dceccf 100644
--- a/trace-recorder.c
+++ b/trace-recorder.c
@@ -115,6 +115,18 @@ void tracecmd_free_recorder(struct tracecmd_recorder *recorder)
free(recorder);
}
+static void set_nonblock(struct tracecmd_recorder *recorder)
+{
+ long flags;
+
+ /* Do not block on reads for flushing */
+ flags = fcntl(recorder->trace_fd, F_GETFL);
+ fcntl(recorder->trace_fd, F_SETFL, flags | O_NONBLOCK);
+
+ /* Do not block on streams for write */
+ recorder->fd_flags |= 2; /* NON_BLOCK */
+}
+
struct tracecmd_recorder *
tracecmd_create_buffer_recorder_fd2(int fd, int fd2, int cpu, unsigned flags,
const char *buffer, int maxkb)
@@ -133,7 +145,7 @@ tracecmd_create_buffer_recorder_fd2(int fd, int fd2, int cpu, unsigned flags,
recorder->fd_flags = 1; /* SPLICE_F_MOVE */
- if (!(recorder->flags & TRACECMD_RECORD_BLOCK))
+ if (!(recorder->flags & TRACECMD_RECORD_BLOCK_SPLICE))
recorder->fd_flags |= 2; /* and NON_BLOCK */
/* Init to know what to free and release */
@@ -192,6 +204,9 @@ tracecmd_create_buffer_recorder_fd2(int fd, int fd2, int cpu, unsigned flags,
recorder->pipe_size = pipe_size;
}
+ if (recorder->flags & TRACECMD_RECORD_POLL)
+ set_nonblock(recorder);
+
free(path);
return recorder;
@@ -421,18 +436,6 @@ static long read_data(struct tracecmd_recorder *recorder)
return r;
}
-static void set_nonblock(struct tracecmd_recorder *recorder)
-{
- long flags;
-
- /* Do not block on reads for flushing */
- flags = fcntl(recorder->trace_fd, F_GETFL);
- fcntl(recorder->trace_fd, F_SETFL, flags | O_NONBLOCK);
-
- /* Do not block on streams for write */
- recorder->fd_flags |= 2; /* NON_BLOCK */
-}
-
long tracecmd_flush_recording(struct tracecmd_recorder *recorder)
{
char buf[recorder->page_size];
diff --git a/trace-usage.c b/trace-usage.c
index dc73d4d9..a9474ad3 100644
--- a/trace-usage.c
+++ b/trace-usage.c
@@ -57,6 +57,7 @@ static struct usage_help usage_help[] = {
" (use with caution)\n"
" --max-graph-depth limit function_graph depth\n"
" --no-filter include trace-cmd threads in the trace\n"
+ " --poll don't block while reading from the trace buffer\n"
},
{
"start",
--
2.31.1

View File

@ -0,0 +1,182 @@
From db8893838433972feafe545117b60dca8c7318df Mon Sep 17 00:00:00 2001
From: Slavomir Kaslev <kaslevs@vmware.com>
Date: Wed, 17 Apr 2019 16:09:58 +0300
Subject: [PATCH 1/2] trace-cmd: Optimize how pid filters are expressed
Express pid filters as allowed/disallowed filter ranges
(pid>=100&&pid<=103)
instead of specifying them per pid
(pid==100||pid==101||pid==102||pid==103)
This makes the size of the resulting filter smaller (and faster) and avoids
overflowing the filter size limit of one page which we can hit on bigger
machines (say >160 CPUs).
Link: http://lore.kernel.org/linux-trace-devel/20190417130959.10064-2-kaslevs@vmware.com
Reported-by: Phil Auld <pauld@redhat.com>
Tested-by: Phil Auld <pauld@redhat.com>
Suggested-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: Slavomir Kaslev <kaslevs@vmware.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
trace-record.c | 117 ++++++++++++++++++++++++++++++++++---------------
1 file changed, 81 insertions(+), 36 deletions(-)
diff --git a/trace-record.c b/trace-record.c
index 9479f5f8073c..e636c97d701d 100644
--- a/trace-record.c
+++ b/trace-record.c
@@ -903,10 +903,63 @@ static void update_ftrace_pids(int reset)
static void update_event_filters(struct buffer_instance *instance);
static void update_pid_event_filters(struct buffer_instance *instance);
+static void append_filter_pid_range(char **filter, int *curr_len,
+ const char *field,
+ int start_pid, int end_pid, bool exclude)
+{
+ const char *op = "", *op1, *op2, *op3;
+ int len;
+
+ if (*filter && **filter)
+ op = exclude ? "&&" : "||";
+
+ /* Handle thus case explicitly so that we get `pid==3` instead of
+ * `pid>=3&&pid<=3` for singleton ranges
+ */
+ if (start_pid == end_pid) {
+#define FMT "%s(%s%s%d)"
+ len = snprintf(NULL, 0, FMT, op,
+ field, exclude ? "!=" : "==", start_pid);
+ *filter = realloc(*filter, *curr_len + len + 1);
+ if (!*filter)
+ die("realloc");
+
+ len = snprintf(*filter + *curr_len, len + 1, FMT, op,
+ field, exclude ? "!=" : "==", start_pid);
+ *curr_len += len;
+
+ return;
+#undef FMT
+ }
+
+ if (exclude) {
+ op1 = "<";
+ op2 = "||";
+ op3 = ">";
+ } else {
+ op1 = ">=";
+ op2 = "&&";
+ op3 = "<=";
+ }
+
+#define FMT "%s(%s%s%d%s%s%s%d)"
+ len = snprintf(NULL, 0, FMT, op,
+ field, op1, start_pid, op2,
+ field, op3, end_pid);
+ *filter = realloc(*filter, *curr_len + len + 1);
+ if (!*filter)
+ die("realloc");
+
+ len = snprintf(*filter + *curr_len, len + 1, FMT, op,
+ field, op1, start_pid, op2,
+ field, op3, end_pid);
+ *curr_len += len;
+}
+
/**
* make_pid_filter - create a filter string to all pids against @field
* @curr_filter: Append to a previous filter (may realloc). Can be NULL
- * @field: The fild to compare the pids against
+ * @field: The field to compare the pids against
*
* Creates a new string or appends to an existing one if @curr_filter
* is not NULL. The new string will contain a filter with all pids
@@ -916,54 +969,46 @@ static void update_pid_event_filters(struct buffer_instance *instance);
*/
static char *make_pid_filter(char *curr_filter, const char *field)
{
+ int start_pid = -1, last_pid = -1;
+ int last_exclude = -1;
struct filter_pids *p;
- char *filter;
- char *orit;
- char *match;
- char *str;
+ char *filter = NULL;
int curr_len = 0;
- int len;
/* Use the new method if possible */
if (have_set_event_pid)
return NULL;
- len = len_filter_pids + (strlen(field) + strlen("(==)||")) * nr_filter_pids;
-
- if (curr_filter) {
- curr_len = strlen(curr_filter);
- filter = realloc(curr_filter, curr_len + len + strlen("(&&())"));
- if (!filter)
- die("realloc");
- memmove(filter+1, curr_filter, curr_len);
- filter[0] = '(';
- strcat(filter, ")&&(");
- curr_len = strlen(filter);
- } else
- filter = malloc(len);
- if (!filter)
- die("Failed to allocate pid filter");
-
- /* Last '||' that is not used will cover the \0 */
- str = filter + curr_len;
+ if (!filter_pids)
+ return curr_filter;
for (p = filter_pids; p; p = p->next) {
- if (p->exclude) {
- match = "!=";
- orit = "&&";
- } else {
- match = "==";
- orit = "||";
+ /*
+ * PIDs are inserted in `filter_pids` from the front and that's
+ * why we expect them in descending order here.
+ */
+ if (p->pid == last_pid - 1 && p->exclude == last_exclude) {
+ last_pid = p->pid;
+ continue;
}
- if (p == filter_pids)
- orit = "";
- len = sprintf(str, "%s(%s%s%d)", orit, field, match, p->pid);
- str += len;
+ if (start_pid != -1)
+ append_filter_pid_range(&filter, &curr_len, field,
+ last_pid, start_pid,
+ last_exclude);
+
+ start_pid = last_pid = p->pid;
+ last_exclude = p->exclude;
+
}
+ append_filter_pid_range(&filter, &curr_len, field,
+ last_pid, start_pid, last_exclude);
- if (curr_len)
- sprintf(str, ")");
+ if (curr_filter) {
+ char *save = filter;
+ asprintf(&filter, "(%s)&&(%s)", curr_filter, filter);
+ free(save);
+ }
return filter;
}
--
2.20.1

View File

@ -4,7 +4,7 @@
Name: trace-cmd
Version: 2.7
Release: 6%{?dist}
Release: 10%{?dist}
License: GPLv2 and LGPLv2
Summary: A user interface to Ftrace
@ -20,6 +20,10 @@ Patch2: trace-cmd-Force-no-build-of-python2-plugin.patch
Patch3: Add-trace-cmd-flightrecorder-service.patch
Patch4: Various-fixes-for-trace-cmd-flightrecorder-systemd.patch
Patch5: trace-cmd-Makefile-Consistent-ELF-application-harden.patch
Patch6: trace-cmd-Optimize-how-pid-filters-are-expressed.patch
Patch7: trace-cmd-Add-no-filter-option-to-not-filter-out-rec.patch
Patch8: tools-lib-traceevent-Fix-missing-equality-check.patch
Patch9: trace-cmd-Add-option-to-poll-trace-buffers.patch
BuildRequires: xmlto
BuildRequires: asciidoc
@ -45,12 +49,7 @@ Kernelshark is the GUI frontend for analyzing data produced by
'trace-cmd extract'
%prep
%setup -q -n %{name}-v%{version}
%patch1 -p1
%patch2 -p1
%patch3 -p1
%patch4 -p1
%patch5 -p1
%autosetup -p1 -n %{name}-v%{version}
%build
# MANPAGE_DOCBOOK_XSL define is hack to avoid using locate
@ -59,7 +58,7 @@ make V=1 CFLAGS="%{optflags} -D_GNU_SOURCE -g -Wall -fPIE -fstack-protector-stro
MANPAGE_DOCBOOK_XSL=$MANPAGE_DOCBOOK_XSL prefix=%{_prefix} all doc gui
%install
make V=1 DESTDIR=%{buildroot}/ prefix=%{_prefix} install install_doc install_gui
make V=1 DESTDIR=%{buildroot}/ prefix=%{_prefix} install install_doc install_gui
find %{buildroot}%{_mandir} -type f | xargs chmod u-x,g-x,o-x
find %{buildroot}%{_datadir} -type f | xargs chmod u-x,g-x,o-x
find %{buildroot}%{_libdir} -type f -iname "*.so" | xargs chmod 0755
@ -91,7 +90,7 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/kernelshark.desktop
%{_libdir}/%{name}/plugins/plugin_tlb.so
%{_libdir}/%{name}/plugins/plugin_xen.so
%{_mandir}/man1/*
%{_mandir}/man1/trace-cmd*
%{_mandir}/man5/*
%{_unitdir}/trace-cmd.service
%{_sysconfdir}/sysconfig/trace-cmd.conf
@ -104,15 +103,30 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/kernelshark.desktop
%{_datadir}/kernelshark
%{_datadir}/applications/kernelshark.desktop
%{_sysconfdir}/bash_completion.d/trace-cmd.bash
%{_mandir}/man1/kernelshark.1.gz
%changelog
* Fri Oct 08 2021 Jerome Marchand <jmarchan@redhat.com> - 2.7-10
- Add poll option
* Wed Feb 03 2021 Jerome Marchand <jmarchan@redhat.com> - 2.7-9
- Filter fixes.
* Fri Apr 26 2019 John Kacur <jkacur@redhat.com> - 2.7-8
- Install kernelshark manpage with kernelshark subpackage, not with trace-cmd
Resolves: rhbz#1678252
* Tue Apr 02 2019 Clark Williams <williams@redhat.com> - 2.7-7
- added OSCI gating framework
Resolves: rhbz#1682420
* Thu Nov 15 2018 John Kacur <jkacur@redhat.com> - 2.7-6
- The changes to the Makefile need to be applied from the specfile too.
Resolves: rhbz#1601040
* Tue Jul 31 2018 John Kacur <jkacur@redhat.com> - 2.7-5
- trace-cmd-Makefile-Consistent-ELF-application-harden.patch
- trace-cmd-Makefile-Consistent-ELF-application-harden.patch
Resolves: rhbz#1601040
* Mon Jun 11 2018 John Kacur <jkacur@redhat.com> - 2.7-4