|
@@ -20,6 +20,7 @@
|
20
|
20
|
FILE_LICENCE ( GPL2_OR_LATER );
|
21
|
21
|
|
22
|
22
|
#include <ipxe/dhcp.h>
|
|
23
|
+#include <ipxe/profile.h>
|
23
|
24
|
#include <pxeparent.h>
|
24
|
25
|
#include <pxe_api.h>
|
25
|
26
|
#include <pxe_types.h>
|
|
@@ -37,6 +38,59 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
37
|
38
|
"External PXE API error" )
|
38
|
39
|
#define EPXECALL( status ) EPLATFORM ( EINFO_EPXECALL, status )
|
39
|
40
|
|
|
41
|
+/** A parent PXE API call profiler */
|
|
42
|
+struct pxeparent_profiler {
|
|
43
|
+ /** Total time spent performing REAL_CALL() */
|
|
44
|
+ struct profiler total;
|
|
45
|
+ /** Time spent transitioning to real mode */
|
|
46
|
+ struct profiler p2r;
|
|
47
|
+ /** Time spent in external code */
|
|
48
|
+ struct profiler ext;
|
|
49
|
+ /** Time spent transitioning back to protected mode */
|
|
50
|
+ struct profiler r2p;
|
|
51
|
+};
|
|
52
|
+
|
|
53
|
+/** PXENV_UNDI_TRANSMIT profiler */
|
|
54
|
+static struct pxeparent_profiler pxeparent_tx_profiler __profiler = {
|
|
55
|
+ { .name = "pxeparent.tx" },
|
|
56
|
+ { .name = "pxeparent.tx_p2r" },
|
|
57
|
+ { .name = "pxeparent.tx_ext" },
|
|
58
|
+ { .name = "pxeparent.tx_r2p" },
|
|
59
|
+};
|
|
60
|
+
|
|
61
|
+/** PXENV_UNDI_ISR profiler
|
|
62
|
+ *
|
|
63
|
+ * Note that this profiler will not see calls to
|
|
64
|
+ * PXENV_UNDI_ISR_IN_START, which are handled by the UNDI ISR and do
|
|
65
|
+ * not go via pxeparent_call().
|
|
66
|
+ */
|
|
67
|
+static struct pxeparent_profiler pxeparent_isr_profiler __profiler = {
|
|
68
|
+ { .name = "pxeparent.isr" },
|
|
69
|
+ { .name = "pxeparent.isr_p2r" },
|
|
70
|
+ { .name = "pxeparent.isr_ext" },
|
|
71
|
+ { .name = "pxeparent.isr_r2p" },
|
|
72
|
+};
|
|
73
|
+
|
|
74
|
+/** PXE unknown API call profiler
|
|
75
|
+ *
|
|
76
|
+ * This profiler can be used to measure the overhead of a dummy PXE
|
|
77
|
+ * API call.
|
|
78
|
+ */
|
|
79
|
+static struct pxeparent_profiler pxeparent_unknown_profiler __profiler = {
|
|
80
|
+ { .name = "pxeparent.unknown" },
|
|
81
|
+ { .name = "pxeparent.unknown_p2r" },
|
|
82
|
+ { .name = "pxeparent.unknown_ext" },
|
|
83
|
+ { .name = "pxeparent.unknown_r2p" },
|
|
84
|
+};
|
|
85
|
+
|
|
86
|
+/** Miscellaneous PXE API call profiler */
|
|
87
|
+static struct pxeparent_profiler pxeparent_misc_profiler __profiler = {
|
|
88
|
+ { .name = "pxeparent.misc" },
|
|
89
|
+ { .name = "pxeparent.misc_p2r" },
|
|
90
|
+ { .name = "pxeparent.misc_ext" },
|
|
91
|
+ { .name = "pxeparent.misc_r2p" },
|
|
92
|
+};
|
|
93
|
+
|
40
|
94
|
/**
|
41
|
95
|
* Name PXE API call
|
42
|
96
|
*
|
|
@@ -103,11 +157,32 @@ pxeparent_function_name ( unsigned int function ) {
|
103
|
157
|
}
|
104
|
158
|
}
|
105
|
159
|
|
|
160
|
+/**
|
|
161
|
+ * Determine applicable profiler pair (for debugging)
|
|
162
|
+ *
|
|
163
|
+ * @v function API call number
|
|
164
|
+ * @ret profiler Profiler
|
|
165
|
+ */
|
|
166
|
+static struct pxeparent_profiler * pxeparent_profiler ( unsigned int function ){
|
|
167
|
+
|
|
168
|
+ /* Determine applicable profiler */
|
|
169
|
+ switch ( function ) {
|
|
170
|
+ case PXENV_UNDI_TRANSMIT:
|
|
171
|
+ return &pxeparent_tx_profiler;
|
|
172
|
+ case PXENV_UNDI_ISR:
|
|
173
|
+ return &pxeparent_isr_profiler;
|
|
174
|
+ case PXENV_UNKNOWN:
|
|
175
|
+ return &pxeparent_unknown_profiler;
|
|
176
|
+ default:
|
|
177
|
+ return &pxeparent_misc_profiler;
|
|
178
|
+ }
|
|
179
|
+}
|
|
180
|
+
|
106
|
181
|
/**
|
107
|
182
|
* PXE parent parameter block
|
108
|
183
|
*
|
109
|
|
- * Used as the paramter block for all parent PXE API calls. Resides in base
|
110
|
|
- * memory.
|
|
184
|
+ * Used as the parameter block for all parent PXE API calls. Resides
|
|
185
|
+ * in base memory.
|
111
|
186
|
*/
|
112
|
187
|
static union u_PXENV_ANY __bss16 ( pxeparent_params );
|
113
|
188
|
#define pxeparent_params __use_data16 ( pxeparent_params )
|
|
@@ -131,8 +206,11 @@ SEGOFF16_t __bss16 ( pxeparent_entry_point );
|
131
|
206
|
*/
|
132
|
207
|
int pxeparent_call ( SEGOFF16_t entry, unsigned int function,
|
133
|
208
|
void *params, size_t params_len ) {
|
|
209
|
+ struct pxeparent_profiler *profiler = pxeparent_profiler ( function );
|
134
|
210
|
PXENV_EXIT_t exit;
|
135
|
|
- int discard_b, discard_D;
|
|
211
|
+ unsigned long started;
|
|
212
|
+ unsigned long stopped;
|
|
213
|
+ int discard_D;
|
136
|
214
|
int rc;
|
137
|
215
|
|
138
|
216
|
/* Copy parameter block and entry point */
|
|
@@ -143,18 +221,31 @@ int pxeparent_call ( SEGOFF16_t entry, unsigned int function,
|
143
|
221
|
/* Call real-mode entry point. This calling convention will
|
144
|
222
|
* work with both the !PXE and the PXENV+ entry points.
|
145
|
223
|
*/
|
|
224
|
+ profile_start ( &profiler->total );
|
146
|
225
|
__asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
|
|
226
|
+ "rdtsc\n\t"
|
|
227
|
+ "pushl %%eax\n\t"
|
147
|
228
|
"pushw %%es\n\t"
|
148
|
229
|
"pushw %%di\n\t"
|
149
|
230
|
"pushw %%bx\n\t"
|
150
|
231
|
"lcall *pxeparent_entry_point\n\t"
|
|
232
|
+ "movw %%ax, %%bx\n\t"
|
|
233
|
+ "rdtsc\n\t"
|
151
|
234
|
"addw $6, %%sp\n\t"
|
|
235
|
+ "popl %%edx\n\t"
|
152
|
236
|
"popl %%ebp\n\t" /* gcc bug */ )
|
153
|
|
- : "=a" ( exit ), "=b" ( discard_b ),
|
154
|
|
- "=D" ( discard_D )
|
|
237
|
+ : "=a" ( stopped ), "=d" ( started ),
|
|
238
|
+ "=b" ( exit ), "=D" ( discard_D )
|
155
|
239
|
: "b" ( function ),
|
156
|
240
|
"D" ( __from_data16 ( &pxeparent_params ) )
|
157
|
|
- : "ecx", "edx", "esi" );
|
|
241
|
+ : "ecx", "esi" );
|
|
242
|
+ profile_stop ( &profiler->total );
|
|
243
|
+ profile_start_at ( &profiler->p2r, profiler->total.started );
|
|
244
|
+ profile_stop_at ( &profiler->p2r, started );
|
|
245
|
+ profile_start_at ( &profiler->ext, started );
|
|
246
|
+ profile_stop_at ( &profiler->ext, stopped );
|
|
247
|
+ profile_start_at ( &profiler->r2p, stopped );
|
|
248
|
+ profile_stop_at ( &profiler->r2p, profiler->total.stopped );
|
158
|
249
|
|
159
|
250
|
/* Determine return status code based on PXENV_EXIT and
|
160
|
251
|
* PXENV_STATUS
|