Explorar el Código

[nvo] Allow resizing of non-volatile stored option blocks

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown hace 14 años
padre
commit
17d28f4877

+ 103
- 49
src/core/nvo.c Ver fichero

@@ -49,6 +49,73 @@ static unsigned int nvo_checksum ( struct nvo_block *nvo ) {
49 49
 	return sum;
50 50
 }
51 51
 
52
+/**
53
+ * Reallocate non-volatile stored options block
54
+ *
55
+ * @v nvo		Non-volatile options block
56
+ * @v len		New length
57
+ * @ret rc		Return status code
58
+ */
59
+static int nvo_realloc ( struct nvo_block *nvo, size_t len ) {
60
+	void *new_data;
61
+
62
+	/* Reallocate data */
63
+	new_data = realloc ( nvo->data, len );
64
+	if ( ! new_data ) {
65
+		DBGC ( nvo, "NVO %p could not allocate %zd bytes\n",
66
+		       nvo, len );
67
+		return -ENOMEM;
68
+	}
69
+	nvo->data = new_data;
70
+	nvo->len = len;
71
+
72
+	/* Update DHCP option block */
73
+	if ( len ) {
74
+		nvo->dhcpopts.data = ( nvo->data + 1 /* checksum */ );
75
+		nvo->dhcpopts.alloc_len = ( len - 1 /* checksum */ );
76
+	} else {
77
+		nvo->dhcpopts.data = NULL;
78
+		nvo->dhcpopts.used_len = 0;
79
+		nvo->dhcpopts.alloc_len = 0;
80
+	}
81
+
82
+	return 0;
83
+}
84
+
85
+/**
86
+ * Reallocate non-volatile stored options DHCP option block
87
+ *
88
+ * @v options		DHCP option block
89
+ * @v len		New length
90
+ * @ret rc		Return status code
91
+ */
92
+static int nvo_realloc_dhcpopt ( struct dhcp_options *options, size_t len ) {
93
+	struct nvo_block *nvo =
94
+		container_of ( options, struct nvo_block, dhcpopts );
95
+	int rc;
96
+
97
+	/* Refuse to reallocate if we have no way to resize the block */
98
+	if ( ! nvo->resize )
99
+		return dhcpopt_no_realloc ( options, len );
100
+
101
+	/* Allow one byte for the checksum (if any data is present) */
102
+	if ( len )
103
+		len += 1;
104
+
105
+	/* Resize underlying non-volatile options block */
106
+	if ( ( rc = nvo->resize ( nvo, len ) ) != 0 ) {
107
+		DBGC ( nvo, "NVO %p could not resize to %zd bytes: %s\n",
108
+		       nvo, len, strerror ( rc ) );
109
+		return rc;
110
+	}
111
+
112
+	/* Reallocate in-memory options block */
113
+	if ( ( rc = nvo_realloc ( nvo, len ) ) != 0 )
114
+		return rc;
115
+
116
+	return 0;
117
+}
118
+
52 119
 /**
53 120
  * Load non-volatile stored options from non-volatile storage device
54 121
  *
@@ -56,8 +123,15 @@ static unsigned int nvo_checksum ( struct nvo_block *nvo ) {
56 123
  * @ret rc		Return status code
57 124
  */
58 125
 static int nvo_load ( struct nvo_block *nvo ) {
126
+	uint8_t *options_data = nvo->dhcpopts.data;
59 127
 	int rc;
60 128
 
129
+	/* Skip reading zero-length NVO fields */
130
+	if ( nvo->len == 0 ) {
131
+		DBGC ( nvo, "NVO %p is empty; skipping load\n", nvo );
132
+		return 0;
133
+	}
134
+
61 135
 	/* Read data */
62 136
 	if ( ( rc = nvs_read ( nvo->nvs, nvo->address, nvo->data,
63 137
 			       nvo->len ) ) != 0 ) {
@@ -66,6 +140,20 @@ static int nvo_load ( struct nvo_block *nvo ) {
66 140
 		return rc;
67 141
 	}
68 142
 
143
+	/* If checksum fails, or options data starts with a zero,
144
+	 * assume the whole block is invalid.  This should capture the
145
+	 * case of random initial contents.
146
+	 */
147
+	if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) {
148
+		DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; "
149
+		       "assuming empty\n", nvo, nvo_checksum ( nvo ),
150
+		       options_data[0] );
151
+		memset ( nvo->data, 0, nvo->len );
152
+	}
153
+
154
+	/* Rescan DHCP option block */
155
+	dhcpopt_update_used_len ( &nvo->dhcpopts );
156
+
69 157
 	DBGC ( nvo, "NVO %p loaded from non-volatile storage\n", nvo );
70 158
 	return 0;
71 159
 }
@@ -80,8 +168,9 @@ static int nvo_save ( struct nvo_block *nvo ) {
80 168
 	uint8_t *checksum = nvo->data;
81 169
 	int rc;
82 170
 
83
-	/* Recalculate checksum */
84
-	*checksum -= nvo_checksum ( nvo );
171
+	/* Recalculate checksum, if applicable */
172
+	if ( nvo->len > 0 )
173
+		*checksum -= nvo_checksum ( nvo );
85 174
 
86 175
 	/* Write data */
87 176
 	if ( ( rc = nvs_write ( nvo->nvs, nvo->address, nvo->data,
@@ -95,38 +184,6 @@ static int nvo_save ( struct nvo_block *nvo ) {
95 184
 	return 0;
96 185
 }
97 186
 
98
-/**
99
- * Parse stored options
100
- *
101
- * @v nvo		Non-volatile options block
102
- *
103
- * Verifies that the options data is valid, and configures the DHCP
104
- * options block.  If the data is not valid, it is replaced with an
105
- * empty options block.
106
- */
107
-static void nvo_init_dhcpopts ( struct nvo_block *nvo ) {
108
-	uint8_t *options_data;
109
-	size_t options_len;
110
-
111
-	/* Steal one byte for the checksum */
112
-	options_data = ( nvo->data + 1 );
113
-	options_len = ( nvo->len - 1 );
114
-
115
-	/* If checksum fails, or options data starts with a zero,
116
-	 * assume the whole block is invalid.  This should capture the
117
-	 * case of random initial contents.
118
-	 */
119
-	if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) {
120
-		DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; "
121
-		       "assuming empty\n", nvo, nvo_checksum ( nvo ),
122
-		       options_data[0] );
123
-		memset ( nvo->data, 0, nvo->len );
124
-	}
125
-
126
-	dhcpopt_init ( &nvo->dhcpopts, options_data, options_len,
127
-		       dhcpopt_no_realloc );
128
-}
129
-
130 187
 /**
131 188
  * Store value of NVO setting
132 189
  *
@@ -190,13 +247,18 @@ static struct settings_operations nvo_settings_operations = {
190 247
  * @v nvs		Underlying non-volatile storage device
191 248
  * @v address		Address within NVS device
192 249
  * @v len		Length of non-volatile options data
250
+ * @v resize		Resize method
193 251
  * @v refcnt		Containing object reference counter, or NULL
194 252
  */
195 253
 void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs,
196
-		size_t address, size_t len, struct refcnt *refcnt ) {
254
+		size_t address, size_t len,
255
+		int ( * resize ) ( struct nvo_block *nvo, size_t len ),
256
+		struct refcnt *refcnt ) {
197 257
 	nvo->nvs = nvs;
198 258
 	nvo->address = address;
199 259
 	nvo->len = len;
260
+	nvo->resize = resize;
261
+	dhcpopt_init ( &nvo->dhcpopts, NULL, 0, nvo_realloc_dhcpopt );
200 262
 	settings_init ( &nvo->settings, &nvo_settings_operations, refcnt, 0 );
201 263
 }
202 264
 
@@ -211,20 +273,14 @@ int register_nvo ( struct nvo_block *nvo, struct settings *parent ) {
211 273
 	int rc;
212 274
 
213 275
 	/* Allocate memory for options */
214
-	nvo->data = zalloc ( nvo->len );
215
-	if ( ! nvo->data ) {
216
-		DBGC ( nvo, "NVO %p could not allocate %zd bytes\n",
217
-		       nvo, nvo->len );
218
-		rc = -ENOMEM;
219
-		goto err_malloc;
220
-	}
276
+	if ( ( rc = nvo_realloc ( nvo, nvo->len ) ) != 0 )
277
+		goto err_realloc;
221 278
 
222 279
 	/* Read data from NVS */
223 280
 	if ( ( rc = nvo_load ( nvo ) ) != 0 )
224 281
 		goto err_load;
225 282
 
226
-	/* Verify and register options */
227
-	nvo_init_dhcpopts ( nvo );
283
+	/* Register settings */
228 284
 	if ( ( rc = register_settings ( &nvo->settings, parent, "nvo" ) ) != 0 )
229 285
 		goto err_register;
230 286
 
@@ -233,9 +289,8 @@ int register_nvo ( struct nvo_block *nvo, struct settings *parent ) {
233 289
 	
234 290
  err_register:
235 291
  err_load:
236
-	free ( nvo->data );
237
-	nvo->data = NULL;
238
- err_malloc:
292
+	nvo_realloc ( nvo, 0 );
293
+ err_realloc:
239 294
 	return rc;
240 295
 }
241 296
 
@@ -246,7 +301,6 @@ int register_nvo ( struct nvo_block *nvo, struct settings *parent ) {
246 301
  */
247 302
 void unregister_nvo ( struct nvo_block *nvo ) {
248 303
 	unregister_settings ( &nvo->settings );
249
-	free ( nvo->data );
250
-	nvo->data = NULL;
304
+	nvo_realloc ( nvo, 0 );
251 305
 	DBGC ( nvo, "NVO %p unregistered\n", nvo );
252 306
 }

+ 1
- 1
src/drivers/net/etherfabric.c Ver fichero

@@ -3273,7 +3273,7 @@ falcon_probe_spi ( struct efab_nic *efab )
3273 3273
 	/* If the device has EEPROM attached, then advertise NVO space */
3274 3274
 	if ( has_eeprom ) {
3275 3275
 		nvo_init ( &efab->nvo, &efab->spi_eeprom.nvs, 0x100, 0xf0,
3276
-			   &efab->netdev->refcnt );
3276
+			   NULL, &efab->netdev->refcnt );
3277 3277
 	}
3278 3278
 
3279 3279
 	return 0;

+ 1
- 0
src/drivers/net/myri10ge.c Ver fichero

@@ -732,6 +732,7 @@ static int myri10ge_nv_init ( struct myri10ge_private *priv )
732 732
 	nvo_init ( &priv->nvo,
733 733
 		   &priv->nvs,
734 734
 		   nvo_fragment_pos, 0x200,
735
+		   NULL,
735 736
 		   & myri10ge_netdev (priv) -> refcnt );
736 737
 	rc = register_nvo ( &priv->nvo,
737 738
 			    netdev_settings ( myri10ge_netdev ( priv ) ) );

+ 1
- 1
src/drivers/net/natsemi.c Ver fichero

@@ -154,7 +154,7 @@ static void natsemi_init_eeprom ( struct natsemi_private *np ) {
154 154
 	 * this region.  Currently it is not working. But with some
155 155
 	 * efforts it can.
156 156
 	 */
157
-	nvo_init ( &np->nvo, &np->eeprom.nvs, 0x0c, 0x68, NULL );
157
+	nvo_init ( &np->nvo, &np->eeprom.nvs, 0x0c, 0x68, NULL, NULL );
158 158
 }
159 159
 
160 160
 /**

+ 1
- 1
src/drivers/net/rtl8139.c Ver fichero

@@ -288,7 +288,7 @@ static void rtl_init_eeprom ( struct net_device *netdev ) {
288 288
 		DBGC ( rtl, "rtl8139 %p EEPROM in use for VPD; cannot use "
289 289
 		       "for options\n", rtl );
290 290
 	} else {
291
-		nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, 0x20, 0x40,
291
+		nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, 0x20, 0x40, NULL,
292 292
 			   &netdev->refcnt );
293 293
 	}
294 294
 }

+ 1
- 0
src/include/ipxe/dhcpopts.h Ver fichero

@@ -36,6 +36,7 @@ extern void dhcpopt_init ( struct dhcp_options *options,
36 36
 			   void *data, size_t alloc_len,
37 37
 			   int ( * realloc ) ( struct dhcp_options *options,
38 38
 					       size_t len ) );
39
+extern void dhcpopt_update_used_len ( struct dhcp_options *options );
39 40
 extern int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len );
40 41
 
41 42
 #endif /* _IPXE_DHCPOPTS_H */

+ 11
- 1
src/include/ipxe/nvo.h Ver fichero

@@ -30,12 +30,22 @@ struct nvo_block {
30 30
 	size_t len;
31 31
 	/** Option-containing data */
32 32
 	void *data;
33
+	/**
34
+	 * Resize non-volatile stored option block
35
+	 *
36
+	 * @v nvo		Non-volatile options block
37
+	 * @v len		New size
38
+	 * @ret rc		Return status code
39
+	 */
40
+	int ( * resize ) ( struct nvo_block *nvo, size_t len );
33 41
 	/** DHCP options block */
34 42
 	struct dhcp_options dhcpopts;
35 43
 };
36 44
 
37 45
 extern void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs,
38
-		       size_t address, size_t len, struct refcnt *refcnt );
46
+		       size_t address, size_t len,
47
+		       int ( * resize ) ( struct nvo_block *nvo, size_t len ),
48
+		       struct refcnt *refcnt );
39 49
 extern int register_nvo ( struct nvo_block *nvo, struct settings *parent );
40 50
 extern void unregister_nvo ( struct nvo_block *nvo );
41 51
 

+ 1
- 1
src/net/dhcpopts.c Ver fichero

@@ -402,7 +402,7 @@ int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag,
402 402
  * The "used length" field will be updated based on scanning through
403 403
  * the block to find the end of the options.
404 404
  */
405
-static void dhcpopt_update_used_len ( struct dhcp_options *options ) {
405
+void dhcpopt_update_used_len ( struct dhcp_options *options ) {
406 406
 	struct dhcp_option *option;
407 407
 	int offset = 0;
408 408
 	ssize_t remaining = options->alloc_len;

Loading…
Cancelar
Guardar