Κυλλήνη

OS X: Migrating to SSD «properly»

Today our target is to migrate OS X from 250GB Hitachi HDD to 160GB OCZ solid-state drive. To do it in proper way we have to take into account a few things:

  • partitions must be aligned to increase drive performance
  • GPT uses unique IDs, that is why we shouldn’t just duplicate all data from one drive to another, but create a new partition table
  • we have to use TRIM for our new SSD to increase drive life
  • it would be nice to enable noatime mount option for filesystems on SSD to minimize writes to it

It is strongly recommended to create a full image backup of your HDD first!
If it all goes wrong this is the only way you are going to get everything back.

Disk upgrade

OK, the first thing to do is to make sure that data will fit into new drive. Because my new SSD is 90GB smaller than the old disk, I had to move excess data to server and remove garbage files. After that I had 140GB used and 100GB free on system HDD — that should fit into 160GB just fine. Now it is time to start Disk Utility and shrink partition(s) to lowest possible size:

Click «Apply» and take a nap — it’s gonna take a while. Then shutdown your Mac and connect both old and new drives to some machine capable of booting Linux locally or from LiveCD, network etc. I used my PC and sysrescuecd distro this time.

Then boot into Linux. You will need: gdisk or parted, dd and optionally smartmontools.

You must find out which drive is assigned to which block device. As long as I am interested in drives health status, I’m gonna use smartmontools:

sysresccd ~ # smartctl --all /dev/sda
smartctl 5.42 2011-10-20 r3458 [x86_64-linux-3.0.13-std241-amd64] (local build)
Copyright (C) 2002-11 by Bruce Allen, http://smartmontools.sourceforge.net

=== START OF INFORMATION SECTION ===
Model Family:     SandForce Driven SSDs
Device Model:     OCZ-VERTEX2
Serial Number:    OCZ-3KRQ8ZRW79C12UC8
LU WWN Device Id: 5 e83a97 f68b340a0
Firmware Version: 1.35
User Capacity:    160,041,885,696 bytes [160 GB]
Sector Size:      512 bytes logical/physical
Device is:        In smartctl database [for details use: -P show]
ATA Version is:   8
ATA Standard is:  ATA-8-ACS revision 6
Local Time is:    Fri Feb 24 10:29:00 2012 UTC
SMART support is: Available - device has SMART capability.
SMART support is: Enabled

--- SNIP ---


sysresccd ~ # smartctl --all /dev/sda
smartctl 5.42 2011-10-20 r3458 [x86_64-linux-3.0.13-std241-amd64] (local build)
Copyright (C) 2002-11 by Bruce Allen, http://smartmontools.sourceforge.net

=== START OF INFORMATION SECTION ===
Device Model:     Hitachi HTS545025B9SA02
Serial Number:    100517PBL2003SK9WWKV
LU WWN Device Id: 5 000cca 5f3eec68b
Firmware Version: PB2AC60W
User Capacity:    250,059,350,016 bytes [250 GB]
Sector Size:      512 bytes logical/physical
Device is:        Not in smartctl database [for details use: -P showall]
ATA Version is:   8
ATA Standard is:  ATA-8-ACS revision 6
Local Time is:    Fri Feb 24 10:28:51 2012 UTC
SMART support is: Available - device has SMART capability.
SMART support is: Enabled

--- SNIP ---

OK, both drives seems to be healthy, it’s awesome. And we know now that /dev/sda is new SSD and /dev/sdb is old HDD. It is crucial to remember this in order not to f*ck everything up.

By the way, it is a good time to upgrade your SSD firmware if necessary ;-)

Next, we have 2 ways – backup GPT partition table to file and restore it on another drive or recreate partitions manually. I prefer the second one.

sysresccd ~ # gdisk /dev/sdb
GPT fdisk (gdisk) version 0.8.1

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.

Command (? for help): b
Enter backup filename to save: /root/oldgpt.bak
The operation has completed successfully.

Command (? for help): q


sysresccd ~ # gdisk /dev/sda
GPT fdisk (gdisk) version 0.8.1

Partition table scan:
  MBR: not present
  BSD: not present
  APM: not present
  GPT: not present

Creating new GPT entries.

Command (? for help): r

Recovery/transformation command (? for help): l
Enter backup filename to load: /root/oldgpt.bak
Warning! Current disk size doesn't match that of the backup!
Adjusting sizes to match, but subsequent problems are possible!

Recovery/transformation command (? for help): p
Disk /dev/sda: 312581808 sectors, 149.1 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 0000414E-7017-0000-2A64-0000176B0000
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 312581774
Partitions will be aligned on 2048-sector boundaries
Total free space is 16699717 sectors (8.0 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1              40          409639   200.0 MiB   EF00  EFI system partition
   2          409640       294612527   140.3 GiB   AF00  Customer
   3       294612528       295882063   619.9 MiB   AB00  Recovery HD

--- SNIP ---

OR manually recreating partitions:

--- SNIP ---

Command (? for help): n
Partition number (1-128, default 1):
First sector (34-312581774, default = 34) or {+-}size{KMGTP}:
Information: Moved requested sector from 34 to 2048 in
order to align on 2048-sector boundaries.
Use 'l' on the experts' menu to adjust alignment
Last sector (2048-312581774, default = 312581774) or {+-}size{KMGTP}: +200M
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300): EF00
Changed type of partition to 'EFI System'

Command (? for help): n
Partition number (2-128, default 2):
First sector (34-312581774, default = 411648) or {+-}size{KMGTP}:
Last sector (411648-312581774, default = 312581774) or {+-}size{KMGTP}: +141G
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300): AF00
Changed type of partition to 'Apple HFS/HFS+'

Command (? for help): n
Partition number (3-128, default 3):
First sector (34-312581774, default = 296110080) or {+-}size{KMGTP}:
Last sector (296110080-312581774, default = 312581774) or {+-}size{KMGTP}: +620M
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300): AB00
Changed type of partition to 'Apple boot'

Command (? for help): c
Partition number (1-2): 1
Enter name: EFI system partition


Command (? for help): p
Disk /dev/sda: 312581808 sectors, 149.1 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): BD4D6CB5-1698-435D-B9A5-F16156D52E92
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 312581774
Partitions will be aligned on 2048-sector boundaries
Total free space is 15203949 sectors (7.2 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          411647   200.0 MiB   EF00  EFI system partition
   2          411648       296110079   141.0 GiB   AF00  Apple HFS/HFS+
   3       296110080       297379839   620.0 MiB   AB00  Apple boot

Command (? for help): c
Partition number (1-3): 2
Enter name: Customer

Command (? for help): c
Partition number (1-3): 3
Enter name: Recovery HD

Command (? for help): p
Disk /dev/sda: 312581808 sectors, 149.1 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): BD4D6CB5-1698-435D-B9A5-F16156D52E92
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 312581774
Partitions will be aligned on 2048-sector boundaries
Total free space is 15203949 sectors (7.2 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          411647   200.0 MiB   EF00  EFI system partition
   2          411648       296110079   141.0 GiB   AF00  Customer
   3       296110080       297379839   620.0 MiB   AB00  Recovery HD

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): Y
OK; writing new GUID partition table (GPT).
The operation has completed successfully.

Now it is time to move filesystems from old drive to the new one:

sysresccd ~ # ls /dev/sd*
/dev/sda  /dev/sda1  /dev/sda2  /dev/sda3  /dev/sdb  /dev/sdb1  /dev/sdb2  /dev/sdb3

sysresccd ~ # dd if=/dev/sdb1 of=/dev/sda1
409600+0 records in
409600+0 records out
209715200 bytes (210 MB) copied, 8.95774 s, 23.4 MB/s

sysresccd ~ # dd if=/dev/sdb3 of=/dev/sda3
1269536+0 records in
1269536+0 records out
650002432 bytes (650 MB) copied, 28.794 s, 22.6 MB/s

sysresccd ~ # dd if=/dev/sdb2 of=/dev/sda2
294202888+0 records in
294202888+0 records out
150631878656 bytes (151 GB) copied, 6528.32 s, 23.1 MB/s

You can install SSD now.

Your Mac might not boot properly now. Do not panic!
Turn your Mac on and hold alt/option (⌥) key to select boot device.
Choose Recovery HD. Start Disk Utility in recovery mode, Verify and Repair your filesystems. No need to expand them now.

Reboot. Open Disk Utility again and expand your partitions as needed (to fill the whole disk in my case).

Now open System Preferences → Startup Disk and choose your new disk as boot device.

Optimizing OS X for SSD

OK now we have a working Mac with SSD in it and properly alligned partitions etc. But to extend solid-state drive life, we have to do some magic.

First, enable TRIM for any SSD. Notice the [REMOVE ME] marker ;-) Yes, remove it:

sudo perl -pi -e 's|(\x52\x6F\x74\x61\x74\x69\x6F\x6E\x61\x6C\x00).{9}(\x00\x51)|$1\x00\x00\x00\x00\x00\x00\x00\x00\x00$2|sg' /System/Library/Extensions/IOAHCIFamily.[REMOVE ME]kext/Contents/PlugIns/IOAHCIBlockStorage.kext/Contents/MacOS/IOAHCIBlockStorage

sudo kextcache -system-prelinked-kernel
sudo kextcache -system-caches

Now create the file /Library/LaunchDaemons/noatime.plist with the following contents:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>noatime</string>
    <key>ProgramArguments</key>
    <array>
      <string>mount</string>
      <string>-uwo</string>
      <string>noatime</string>
      <string>/</string>
    </array>
    <key>RunAtLoad</key>
    <true></true>
  </dict>
</plist>

Reboot, verify:

user@mcbpro ~ $ mount | grep " / "
/dev/disk0s2 on / (hfs, local, journaled, noatime)

Mount option noatime is enabled. And System Information → Serial ATA says that TRIM is also enabled:

That’s it!

Links: