| 1181 | struct VolumeInfo { |
| 1182 | char name[B_FILE_NAME_LENGTH]; |
| 1183 | char device[B_FILE_NAME_LENGTH]; |
| 1184 | char filesystem[B_OS_NAME_LENGTH]; |
| 1185 | off_t capacity; |
| 1186 | }; |
| 1187 | |
| 1188 | |
| 1189 | class PartitionScorer : public KPartitionVisitor { |
| 1190 | public: |
| 1191 | PartitionScorer(VolumeInfo& volumeInfo) |
| 1192 | : |
| 1193 | fBestPartition(NULL), |
| 1194 | fBestScore(-1), |
| 1195 | fVolumeInfo(volumeInfo) |
| 1196 | { |
| 1197 | } |
| 1198 | |
| 1199 | virtual bool VisitPre(KPartition* partition) |
| 1200 | { |
| 1201 | if (!partition->ContainsFileSystem()) |
| 1202 | return false; |
| 1203 | |
| 1204 | KPath path; |
| 1205 | partition->GetPath(&path); |
| 1206 | |
| 1207 | int score = 0; |
| 1208 | if (strcmp(fVolumeInfo.name, partition->ContentName()) == 0) |
| 1209 | score += 4; |
| 1210 | if (strcmp(fVolumeInfo.device, path.Path()) == 0) |
| 1211 | score += 3; |
| 1212 | if (fVolumeInfo.capacity == partition->Size()) |
| 1213 | score += 2; |
| 1214 | if (strcmp(fVolumeInfo.filesystem, |
| 1215 | partition->DiskSystem()->ShortName()) == 0) { |
| 1216 | score += 1; |
| 1217 | } |
| 1218 | if (score >= 4 && score > fBestScore) { |
| 1219 | fBestPartition = partition; |
| 1220 | fBestScore = score; |
| 1221 | } |
| 1222 | |
| 1223 | return false; |
| 1224 | } |
| 1225 | |
| 1226 | KPartition* fBestPartition; |
| 1227 | |
| 1228 | private: |
| 1229 | int32 fBestScore; |
| 1230 | VolumeInfo fVolumeInfo; |
| 1231 | }; |
| 1232 | |
| 1233 | |
| 1234 | status_t |
| 1235 | get_mount_point(KPartition* partition, KPath* mountPoint) |
| 1236 | { |
| 1237 | if (!mountPoint || !partition->ContainsFileSystem()) |
| 1238 | return B_BAD_VALUE; |
| 1239 | |
| 1240 | const char* volumeName = partition->ContentName(); |
| 1241 | if (!volumeName || strlen(volumeName) == 0) |
| 1242 | volumeName = partition->Name(); |
| 1243 | if (!volumeName || strlen(volumeName) == 0) |
| 1244 | volumeName = "unnamed volume"; |
| 1245 | |
| 1246 | char basePath[B_PATH_NAME_LENGTH]; |
| 1247 | int32 len = snprintf(basePath, sizeof(basePath), "/%s", volumeName); |
| 1248 | for (int32 i = 1; i < len; i++) |
| 1249 | if (basePath[i] == '/') |
| 1250 | basePath[i] = '-'; |
| 1251 | char* path = mountPoint->LockBuffer(); |
| 1252 | int32 pathLen = mountPoint->BufferSize(); |
| 1253 | strncpy(path, basePath, pathLen); |
| 1254 | |
| 1255 | struct stat dummy; |
| 1256 | for (int i = 1; ; i++) { |
| 1257 | if (stat(path, &dummy) != 0) |
| 1258 | break; |
| 1259 | snprintf(path, pathLen, "%s%d", basePath, i); |
| 1260 | } |
| 1261 | |
| 1262 | mountPoint->UnlockBuffer(); |
| 1263 | return B_OK; |
| 1264 | } |
| 1265 | |
| 1266 | |
1347 | | if (!get_driver_boolean_parameter(settings, "vm", false, false)) { |
1348 | | unload_driver_settings(settings); |
1349 | | return; |
| 1448 | // We pass a lot of information on the swap device, this is mostly to |
| 1449 | // ensure that we are dealing with the same device that was configured. |
| 1450 | |
| 1451 | // TODO: Some kind of BFS uuid would be great here :) |
| 1452 | const char* enabled = get_driver_parameter(settings, "vm", NULL, NULL); |
| 1453 | |
| 1454 | if (enabled != NULL) { |
| 1455 | swapEnabled = get_driver_boolean_parameter(settings, "vm", |
| 1456 | false, false); |
| 1457 | |
| 1458 | const char* size = get_driver_parameter(settings, "swap_size", |
| 1459 | NULL, NULL); |
| 1460 | const char* volume = get_driver_parameter(settings, |
| 1461 | "swap_volume_name", NULL, NULL); |
| 1462 | const char* device = get_driver_parameter(settings, |
| 1463 | "swap_volume_device", NULL, NULL); |
| 1464 | const char* filesystem = get_driver_parameter(settings, |
| 1465 | "swap_volume_filesystem", NULL, NULL); |
| 1466 | const char* capacity = get_driver_parameter(settings, |
| 1467 | "swap_volume_capacity", NULL, NULL); |
| 1468 | |
| 1469 | if (size != NULL && device != NULL && volume != NULL |
| 1470 | && filesystem != NULL && capacity != NULL) { |
| 1471 | // User specified a size / volume |
| 1472 | swapAutomatic = false; |
| 1473 | swapSize = atoll(size); |
| 1474 | strncpy(selectedVolume.name, volume, |
| 1475 | sizeof(selectedVolume.name)); |
| 1476 | strncpy(selectedVolume.device, device, |
| 1477 | sizeof(selectedVolume.device)); |
| 1478 | strncpy(selectedVolume.filesystem, filesystem, |
| 1479 | sizeof(selectedVolume.filesystem)); |
| 1480 | selectedVolume.capacity = atoll(capacity); |
| 1481 | } |
1356 | | unload_driver_settings(settings); |
| 1491 | if (!swapEnabled || swapSize < B_PAGE_SIZE) |
| 1492 | return; |
| 1493 | |
| 1494 | dev_t dev = -1; |
| 1495 | |
| 1496 | if (!swapAutomatic) { |
| 1497 | KDiskDeviceManager::CreateDefault(); |
| 1498 | KDiskDeviceManager* manager = KDiskDeviceManager::Default(); |
| 1499 | PartitionScorer visitor(selectedVolume); |
| 1500 | |
| 1501 | KDiskDevice* device; |
| 1502 | int32 cookie = 0; |
| 1503 | while ((device = manager->NextDevice(&cookie)) != NULL) { |
| 1504 | if (device->IsReadOnlyMedia() || device->IsWriteOnce() |
| 1505 | || device->IsRemovable()) { |
| 1506 | continue; |
| 1507 | } |
| 1508 | device->VisitEachDescendant(&visitor); |
| 1509 | } |
| 1510 | |
| 1511 | if (!visitor.fBestPartition) { |
| 1512 | dprintf("%s: Can't find configured swap partition '%s'\n", |
| 1513 | __func__, selectedVolume.name); |
| 1514 | } else { |
| 1515 | if (visitor.fBestPartition->IsMounted()) |
| 1516 | dev = visitor.fBestPartition->VolumeID(); |
| 1517 | else { |
| 1518 | KPath devPath, mountPoint; |
| 1519 | visitor.fBestPartition->GetPath(&devPath); |
| 1520 | get_mount_point(visitor.fBestPartition, &mountPoint); |
| 1521 | const char* mountPath = mountPoint.Path(); |
| 1522 | mkdir(mountPath, S_IRWXU | S_IRWXG | S_IRWXO); |
| 1523 | dev = _kern_mount(mountPath, devPath.Path(), |
| 1524 | NULL, 0, NULL, 0); |
| 1525 | if (dev < 0) { |
| 1526 | dprintf("%s: Can't mount configured swap partition '%s'\n", |
| 1527 | __func__, selectedVolume.name); |
| 1528 | } |
| 1529 | } |
| 1530 | } |
| 1531 | } |
| 1532 | |
| 1533 | if (dev < 0) |
| 1534 | dev = gBootDevice; |
| 1535 | |
| 1536 | KPath path; |
| 1537 | struct fs_info info; |
| 1538 | _kern_read_fs_info(dev, &info); |
| 1539 | if (dev == gBootDevice) |
| 1540 | path = kDefaultSwapPath; |
| 1541 | else { |
| 1542 | vfs_entry_ref_to_path(info.dev, info.root, |
| 1543 | ".", path.LockBuffer(), path.BufferSize()); |
| 1544 | path.UnlockBuffer(); |
| 1545 | path.Append("swap"); |
| 1546 | } |
| 1547 | |
| 1548 | const char* swapPath = path.Path(); |
| 1549 | |
| 1550 | // Swap size limits prevent oversized swap files |
| 1551 | off_t existingSwapSize = 0; |
| 1552 | struct stat existingSwapStat; |
| 1553 | if (stat(swapPath, &existingSwapStat) == 0) |
| 1554 | existingSwapSize = existingSwapStat.st_size; |
| 1555 | |
| 1556 | off_t freeSpace = info.free_blocks * info.block_size + existingSwapSize; |
| 1557 | off_t maxSwap = freeSpace; |
| 1558 | if (swapAutomatic) { |
| 1559 | // Adjust automatic swap to a maximum of 25% of the free space |
| 1560 | maxSwap = (off_t)(0.25 * freeSpace); |