diff options
Diffstat (limited to 'drivers/mtd/ubi/fastmap.c')
-rw-r--r-- | drivers/mtd/ubi/fastmap.c | 162 |
1 files changed, 114 insertions, 48 deletions
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c index 762e25c6a2d2..db2625d3ecd9 100644 --- a/drivers/mtd/ubi/fastmap.c +++ b/drivers/mtd/ubi/fastmap.c | |||
@@ -1300,31 +1300,87 @@ out: | |||
1300 | /** | 1300 | /** |
1301 | * invalidate_fastmap - destroys a fastmap. | 1301 | * invalidate_fastmap - destroys a fastmap. |
1302 | * @ubi: UBI device object | 1302 | * @ubi: UBI device object |
1303 | * @fm: the fastmap to be destroyed | ||
1304 | * | 1303 | * |
1304 | * This function ensures that upon next UBI attach a full scan | ||
1305 | * is issued. We need this if UBI is about to write a new fastmap | ||
1306 | * but is unable to do so. In this case we have two options: | ||
1307 | * a) Make sure that the current fastmap will not be usued upon | ||
1308 | * attach time and contine or b) fall back to RO mode to have the | ||
1309 | * current fastmap in a valid state. | ||
1305 | * Returns 0 on success, < 0 indicates an internal error. | 1310 | * Returns 0 on success, < 0 indicates an internal error. |
1306 | */ | 1311 | */ |
1307 | static int invalidate_fastmap(struct ubi_device *ubi, | 1312 | static int invalidate_fastmap(struct ubi_device *ubi) |
1308 | struct ubi_fastmap_layout *fm) | ||
1309 | { | 1313 | { |
1310 | int ret; | 1314 | int ret; |
1311 | struct ubi_vid_hdr *vh; | 1315 | struct ubi_fastmap_layout *fm; |
1316 | struct ubi_wl_entry *e; | ||
1317 | struct ubi_vid_hdr *vh = NULL; | ||
1312 | 1318 | ||
1313 | ret = erase_block(ubi, fm->e[0]->pnum); | 1319 | if (!ubi->fm) |
1314 | if (ret < 0) | 1320 | return 0; |
1315 | return ret; | 1321 | |
1322 | ubi->fm = NULL; | ||
1323 | |||
1324 | ret = -ENOMEM; | ||
1325 | fm = kzalloc(sizeof(*fm), GFP_KERNEL); | ||
1326 | if (!fm) | ||
1327 | goto out; | ||
1316 | 1328 | ||
1317 | vh = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID); | 1329 | vh = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID); |
1318 | if (!vh) | 1330 | if (!vh) |
1319 | return -ENOMEM; | 1331 | goto out_free_fm; |
1332 | |||
1333 | ret = -ENOSPC; | ||
1334 | e = ubi_wl_get_fm_peb(ubi, 1); | ||
1335 | if (!e) | ||
1336 | goto out_free_fm; | ||
1320 | 1337 | ||
1321 | /* deleting the current fastmap SB is not enough, an old SB may exist, | 1338 | /* |
1322 | * so create a (corrupted) SB such that fastmap will find it and fall | 1339 | * Create fake fastmap such that UBI will fall back |
1323 | * back to scanning mode in any case */ | 1340 | * to scanning mode. |
1341 | */ | ||
1324 | vh->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); | 1342 | vh->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); |
1325 | ret = ubi_io_write_vid_hdr(ubi, fm->e[0]->pnum, vh); | 1343 | ret = ubi_io_write_vid_hdr(ubi, e->pnum, vh); |
1344 | if (ret < 0) { | ||
1345 | ubi_wl_put_fm_peb(ubi, e, 0, 0); | ||
1346 | goto out_free_fm; | ||
1347 | } | ||
1326 | 1348 | ||
1349 | fm->used_blocks = 1; | ||
1350 | fm->e[0] = e; | ||
1351 | |||
1352 | ubi->fm = fm; | ||
1353 | |||
1354 | out: | ||
1355 | ubi_free_vid_hdr(ubi, vh); | ||
1327 | return ret; | 1356 | return ret; |
1357 | |||
1358 | out_free_fm: | ||
1359 | kfree(fm); | ||
1360 | goto out; | ||
1361 | } | ||
1362 | |||
1363 | /** | ||
1364 | * return_fm_pebs - returns all PEBs used by a fastmap back to the | ||
1365 | * WL sub-system. | ||
1366 | * @ubi: UBI device object | ||
1367 | * @fm: fastmap layout object | ||
1368 | */ | ||
1369 | static void return_fm_pebs(struct ubi_device *ubi, | ||
1370 | struct ubi_fastmap_layout *fm) | ||
1371 | { | ||
1372 | int i; | ||
1373 | |||
1374 | if (!fm) | ||
1375 | return; | ||
1376 | |||
1377 | for (i = 0; i < fm->used_blocks; i++) { | ||
1378 | if (fm->e[i]) { | ||
1379 | ubi_wl_put_fm_peb(ubi, fm->e[i], i, | ||
1380 | fm->to_be_tortured[i]); | ||
1381 | fm->e[i] = NULL; | ||
1382 | } | ||
1383 | } | ||
1328 | } | 1384 | } |
1329 | 1385 | ||
1330 | /** | 1386 | /** |
@@ -1336,7 +1392,7 @@ static int invalidate_fastmap(struct ubi_device *ubi, | |||
1336 | */ | 1392 | */ |
1337 | int ubi_update_fastmap(struct ubi_device *ubi) | 1393 | int ubi_update_fastmap(struct ubi_device *ubi) |
1338 | { | 1394 | { |
1339 | int ret, i; | 1395 | int ret, i, j; |
1340 | struct ubi_fastmap_layout *new_fm, *old_fm; | 1396 | struct ubi_fastmap_layout *new_fm, *old_fm; |
1341 | struct ubi_wl_entry *tmp_e; | 1397 | struct ubi_wl_entry *tmp_e; |
1342 | 1398 | ||
@@ -1376,34 +1432,40 @@ int ubi_update_fastmap(struct ubi_device *ubi) | |||
1376 | tmp_e = ubi_wl_get_fm_peb(ubi, 0); | 1432 | tmp_e = ubi_wl_get_fm_peb(ubi, 0); |
1377 | spin_unlock(&ubi->wl_lock); | 1433 | spin_unlock(&ubi->wl_lock); |
1378 | 1434 | ||
1379 | if (!tmp_e && !old_fm) { | 1435 | if (!tmp_e) { |
1380 | int j; | 1436 | if (old_fm && old_fm->e[i]) { |
1381 | ubi_err(ubi, "could not get any free erase block"); | 1437 | ret = erase_block(ubi, old_fm->e[i]->pnum); |
1382 | 1438 | if (ret < 0) { | |
1383 | for (j = 1; j < i; j++) | 1439 | ubi_err(ubi, "could not erase old fastmap PEB"); |
1384 | ubi_wl_put_fm_peb(ubi, new_fm->e[j], j, 0); | 1440 | |
1385 | 1441 | for (j = 1; j < i; j++) { | |
1386 | ret = -ENOSPC; | 1442 | ubi_wl_put_fm_peb(ubi, new_fm->e[j], |
1387 | goto err; | 1443 | j, 0); |
1388 | } else if (!tmp_e && old_fm && old_fm->e[i]) { | 1444 | new_fm->e[j] = NULL; |
1389 | ret = erase_block(ubi, old_fm->e[i]->pnum); | 1445 | } |
1390 | if (ret < 0) { | 1446 | goto err; |
1391 | int j; | 1447 | } |
1392 | 1448 | new_fm->e[i] = old_fm->e[i]; | |
1393 | for (j = 1; j < i; j++) | 1449 | old_fm->e[i] = NULL; |
1394 | ubi_wl_put_fm_peb(ubi, new_fm->e[j], | 1450 | } else { |
1395 | j, 0); | 1451 | ubi_err(ubi, "could not get any free erase block"); |
1452 | |||
1453 | for (j = 1; j < i; j++) { | ||
1454 | ubi_wl_put_fm_peb(ubi, new_fm->e[j], j, 0); | ||
1455 | new_fm->e[j] = NULL; | ||
1456 | } | ||
1396 | 1457 | ||
1397 | ubi_err(ubi, "could not erase old fastmap PEB"); | 1458 | ret = -ENOSPC; |
1398 | goto err; | 1459 | goto err; |
1399 | } | 1460 | } |
1400 | new_fm->e[i] = old_fm->e[i]; | ||
1401 | } else { | 1461 | } else { |
1402 | new_fm->e[i] = tmp_e; | 1462 | new_fm->e[i] = tmp_e; |
1403 | 1463 | ||
1404 | if (old_fm && old_fm->e[i]) | 1464 | if (old_fm && old_fm->e[i]) { |
1405 | ubi_wl_put_fm_peb(ubi, old_fm->e[i], i, | 1465 | ubi_wl_put_fm_peb(ubi, old_fm->e[i], i, |
1406 | old_fm->to_be_tortured[i]); | 1466 | old_fm->to_be_tortured[i]); |
1467 | old_fm->e[i] = NULL; | ||
1468 | } | ||
1407 | } | 1469 | } |
1408 | } | 1470 | } |
1409 | 1471 | ||
@@ -1412,6 +1474,7 @@ int ubi_update_fastmap(struct ubi_device *ubi) | |||
1412 | for (i = new_fm->used_blocks; i < old_fm->used_blocks; i++) { | 1474 | for (i = new_fm->used_blocks; i < old_fm->used_blocks; i++) { |
1413 | ubi_wl_put_fm_peb(ubi, old_fm->e[i], i, | 1475 | ubi_wl_put_fm_peb(ubi, old_fm->e[i], i, |
1414 | old_fm->to_be_tortured[i]); | 1476 | old_fm->to_be_tortured[i]); |
1477 | old_fm->e[i] = NULL; | ||
1415 | } | 1478 | } |
1416 | } | 1479 | } |
1417 | 1480 | ||
@@ -1424,29 +1487,33 @@ int ubi_update_fastmap(struct ubi_device *ubi) | |||
1424 | if (!tmp_e) { | 1487 | if (!tmp_e) { |
1425 | ret = erase_block(ubi, old_fm->e[0]->pnum); | 1488 | ret = erase_block(ubi, old_fm->e[0]->pnum); |
1426 | if (ret < 0) { | 1489 | if (ret < 0) { |
1427 | int i; | ||
1428 | ubi_err(ubi, "could not erase old anchor PEB"); | 1490 | ubi_err(ubi, "could not erase old anchor PEB"); |
1429 | 1491 | ||
1430 | for (i = 1; i < new_fm->used_blocks; i++) | 1492 | for (i = 1; i < new_fm->used_blocks; i++) { |
1431 | ubi_wl_put_fm_peb(ubi, new_fm->e[i], | 1493 | ubi_wl_put_fm_peb(ubi, new_fm->e[i], |
1432 | i, 0); | 1494 | i, 0); |
1495 | new_fm->e[i] = NULL; | ||
1496 | } | ||
1433 | goto err; | 1497 | goto err; |
1434 | } | 1498 | } |
1435 | new_fm->e[0] = old_fm->e[0]; | 1499 | new_fm->e[0] = old_fm->e[0]; |
1436 | new_fm->e[0]->ec = ret; | 1500 | new_fm->e[0]->ec = ret; |
1501 | old_fm->e[0] = NULL; | ||
1437 | } else { | 1502 | } else { |
1438 | /* we've got a new anchor PEB, return the old one */ | 1503 | /* we've got a new anchor PEB, return the old one */ |
1439 | ubi_wl_put_fm_peb(ubi, old_fm->e[0], 0, | 1504 | ubi_wl_put_fm_peb(ubi, old_fm->e[0], 0, |
1440 | old_fm->to_be_tortured[0]); | 1505 | old_fm->to_be_tortured[0]); |
1441 | new_fm->e[0] = tmp_e; | 1506 | new_fm->e[0] = tmp_e; |
1507 | old_fm->e[0] = NULL; | ||
1442 | } | 1508 | } |
1443 | } else { | 1509 | } else { |
1444 | if (!tmp_e) { | 1510 | if (!tmp_e) { |
1445 | int i; | ||
1446 | ubi_err(ubi, "could not find any anchor PEB"); | 1511 | ubi_err(ubi, "could not find any anchor PEB"); |
1447 | 1512 | ||
1448 | for (i = 1; i < new_fm->used_blocks; i++) | 1513 | for (i = 1; i < new_fm->used_blocks; i++) { |
1449 | ubi_wl_put_fm_peb(ubi, new_fm->e[i], i, 0); | 1514 | ubi_wl_put_fm_peb(ubi, new_fm->e[i], i, 0); |
1515 | new_fm->e[i] = NULL; | ||
1516 | } | ||
1450 | 1517 | ||
1451 | ret = -ENOSPC; | 1518 | ret = -ENOSPC; |
1452 | goto err; | 1519 | goto err; |
@@ -1469,19 +1536,18 @@ out_unlock: | |||
1469 | return ret; | 1536 | return ret; |
1470 | 1537 | ||
1471 | err: | 1538 | err: |
1472 | kfree(new_fm); | ||
1473 | |||
1474 | ubi_warn(ubi, "Unable to write new fastmap, err=%i", ret); | 1539 | ubi_warn(ubi, "Unable to write new fastmap, err=%i", ret); |
1475 | 1540 | ||
1476 | ret = 0; | 1541 | ret = invalidate_fastmap(ubi); |
1477 | if (old_fm) { | 1542 | if (ret < 0) { |
1478 | ret = invalidate_fastmap(ubi, old_fm); | 1543 | ubi_err(ubi, "Unable to invalidiate current fastmap!"); |
1479 | if (ret < 0) { | 1544 | ubi_ro_mode(ubi); |
1480 | ubi_err(ubi, "Unable to invalidiate current fastmap!"); | 1545 | } else { |
1481 | ubi_ro_mode(ubi); | 1546 | return_fm_pebs(ubi, old_fm); |
1482 | } | 1547 | return_fm_pebs(ubi, new_fm); |
1483 | else if (ret) | 1548 | ret = 0; |
1484 | ret = 0; | ||
1485 | } | 1549 | } |
1550 | |||
1551 | kfree(new_fm); | ||
1486 | goto out_unlock; | 1552 | goto out_unlock; |
1487 | } | 1553 | } |