1#!/bin/sh
2
3# PROVIDE: ec2_ephemeralswap
4# REQUIRE: NETWORKING
5# BEFORE: savecore
6
7# Define ec2_ephemeralswap_enable=YES in /etc/rc.conf to enable slicing
8# of the ephemeral disks to create swap space when the system next boots.
9#
10# Define ec2_ephemeralswap_size=N in /etc/rc.conf to use N MB of swap space
11# instead of auto-sizing based on the amount of RAM.
12#
13: ${ec2_ephemeralswap_enable=NO}
14: ${ec2_ephemeralswap_size=AUTO}
15
16. /etc/rc.subr
17
18name="ec2_ephemeralswap"
19rcvar=ec2_ephemeralswap_enable
20start_cmd="ec2_ephemeralswap_run"
21stop_cmd=":"
22
23ec2_ephemeralswap_run()
24{
25	local RAM RAMMB SWAPMB SWZONESTRUCTS SWZONEMAXMB
26	local EC2DISKS EC2DISK BLKDEV SWAPDISKS NSWAPDISKS
27	local SWAPPERDISK SWAPDISK
28
29	# Compute "ideal" swap size:
30	#     2*RAM if RAM <= 4 GB
31	#     8 GB  if 4 GB <= RAM <= 8 GB
32	#     RAM   if 8 GB <= RAM
33	RAM=`sysctl -n hw.physmem`
34	RAMMB=`expr $RAM / 1048576`
35	if [ $RAMMB -lt 4096 ]; then
36		SWAPMB=`expr $RAMMB \* 2`
37	elif [ $RAMMB -lt 8192 ]; then
38		SWAPMB=8192
39	else
40		SWAPMB=$RAMMB
41	fi
42
43	# If a swap size was specified, use that instead of our "ideal" value.
44	case ${ec2_ephemeralswap_size} in
45	[Aa][Uu][Tt][Oo])
46		;;
47	*)
48		SWAPMB=${ec2_ephemeralswap_size}
49		;;
50	esac
51
52	# Reduce this size if the kernel hasn't reserved enough space to
53	# keep track of that much swap.
54	SWZONESTRUCTS=`vmstat -z | tr -d , | awk '/^SWAPMETA/ { print $3 }'`
55	SWZONEMAXMB=`expr $SWZONESTRUCTS / 16`
56	if [ $SWZONEMAXMB -lt $SWAPMB ]; then
57		echo -n "Reducing ephemeral swap target from $SWAPMB MB to"
58		echo -n " $SWZONEMAXMB MB since kernel SWAPMETA zone is"
59		echo " too small."
60		SWAPMB=$SWZONEMAXMB
61	fi
62
63	# Fetch a list of EC2 disks
64	EC2DISKS=`fetch -qo - http://169.254.169.254/latest/meta-data/block-device-mapping/ | grep -E '^ephemeral[0-9]+$'`
65	debug "EC2 ephemeral disks are $EC2DISKS"
66
67	# Figure out where they're mapped to
68	SWAPDISKS=""
69	NSWAPDISKS=0
70	for EC2DISK in $EC2DISKS; do
71		BLKDEV=`fetch -qo - http://169.254.169.254/latest/meta-data/block-device-mapping/$EC2DISK | sed 's|/dev/||'`
72		case "$BLKDEV" in
73		sd[a-j])
74			SWAPDISK="/dev/xbd`echo $BLKDEV | cut -c 3 | tr 'a-j' '0-9'`"
75			;;
76		xvd[a-z])
77			SWAPDISK="/dev/xbd`echo $BLKDEV | cut -c 4 | tr 'a-j' '0-9'`"
78			;;
79		*)
80			echo "Can't translate $EC2DISK to a FreeBSD device name"
81			continue
82			;;
83		esac
84		if [ -c $SWAPDISK ]; then
85			SWAPDISKS="$SWAPDISKS $SWAPDISK"
86			NSWAPDISKS=$(($NSWAPDISKS + 1))
87		else
88			debug "Ephemeral swap disk $SWAPDISK doesn't exist"
89		fi
90	done
91
92	# If we have no ephemeral disks, give up.
93	if [ $NSWAPDISKS -eq 0 ]; then
94		echo -n "No ephemeral disks are available,"
95		echo " so no swap space is being created."
96		return 0;
97	fi
98
99	# How much swap space do we want per disk?
100	SWAPPERDISK=`expr $SWAPMB / $NSWAPDISKS`
101	if [ $SWAPPERDISK -gt 32768 ]; then
102		# FreeBSD swap_pager code can't use more than 32 GB per device
103		SWAPPERDISK=32768
104	fi
105
106	debug "Want $SWAPPERDISK MB swap on each of $NSWAPDISKS disks: $SWAPDISKS"
107
108	# Slice and partition disks
109	for SWAPDISK in $SWAPDISKS; do
110		if [ `ls $SWAPDISK[a-z.]* 2>/dev/null | wc -l` -gt 1 ]; then
111			debug "Ephemeral swap disk $SWAPDISK already in use"
112			continue;
113		fi
114
115		# Figure out how large the disk is.  Subtract off 16 MB for
116		# losses to "cylinder" alignment etc.
117		DISKSZ=`diskinfo $SWAPDISK | awk '{ print $3 }'`
118		DISKSZM=`expr $DISKSZ / 1048576 - 16`
119
120		# Use the size we want or the size we've got, whichever is less
121		if [ $DISKSZM -lt $SWAPPERDISK ]; then
122			SWAPSZ=$DISKSZM;
123		else
124			SWAPSZ=$SWAPPERDISK;
125		fi
126
127		# Try to create a $SWAPSZ MB slice on this disk.
128		echo -n "Attempting to create a $SWAPSZ MB swap area on $SWAPDISK..."
129		( echo "p 1 0xa5 64 ${SWAPSZ}M"; echo "p 2 0xa5 * *" ) |
130		    fdisk -i -f /dev/stdin $SWAPDISK >/dev/null 2>/dev/null
131
132		# Attempt to create a swap partition on the new slice.
133		( echo "b: * 0 swap"; echo "c: * 0 unused" ) |
134		    bsdlabel -R ${SWAPDISK}s1 /dev/stdin
135
136		# Did we succeed?
137		if [ -c ${SWAPDISK}s1b ]; then
138			echo " done."
139		else
140			echo " failed."
141		fi
142	done
143
144	# Turn on swap
145	for SWAPDISK in $SWAPDISKS; do
146		# If there isn't an s1b partition, this isn't one of ours.
147		if ! [ -c "${SWAPDISK}s1b" ]; then
148			continue;
149		fi
150
151		# Make sure that s1b is a swap partition.
152		if ! bsdlabel ${SWAPDISK}s1 | grep b: | grep -q swap; then
153			continue;
154		fi
155
156		# Enable swap on this disk
157		echo "Enabling swapping to ${SWAPDISK}s1b"
158		swapon ${SWAPDISK}s1b
159	done
160
161	# Turn on dumping, if ${dumpdev} is AUTO (the dumpon rc.d script runs
162	# early in the boot process, but we can't run until the network has
163	# been brought up -- so we duplicate here some of the work which that
164	# script does.
165	case ${dumpdev} in
166	[Aa][Uu][Tt][Oo])
167		for SWAPDISK in $SWAPDISKS; do
168			# Skip if this is not a disk we're swapping to.
169			if ! swapinfo | grep -Eq "^${SWAPDISK}s1b"; then
170				continue;
171			fi
172
173			# If we don't have a dump disk yet, use this one.
174			if ! [ -e /dev/dumpdev ]; then
175				echo "Enabling crash dumps to ${SWAPDISK}s1b"
176				dumpon ${SWAPDISK}s1b
177				ln -sf ${SWAPDISK}s1b /dev/dumpdev
178			fi
179		done
180		;;
181	esac
182}
183
184load_rc_config $name
185run_rc_command "$1"
186