CLOUDSTACK-1302: Allow a cache mode per disk offering

Per disk offering the setting none, writeback or writethrough can be set

This allows for both safety and performance for writes.
diff --git a/api/src/com/cloud/agent/api/to/VolumeTO.java b/api/src/com/cloud/agent/api/to/VolumeTO.java
index fb08384..4dec4d8 100644
--- a/api/src/com/cloud/agent/api/to/VolumeTO.java
+++ b/api/src/com/cloud/agent/api/to/VolumeTO.java
@@ -41,6 +41,7 @@
     private Long bytesWriteRate;
     private Long iopsReadRate;
     private Long iopsWriteRate;
+    private String cacheMode;
     private Long chainSize;
 
     public VolumeTO(long id, Volume.Type type, StoragePoolType poolType, String poolUuid, String name, String mountPoint, String path, long size, String chainInfo) {
@@ -176,6 +177,14 @@
         return iopsWriteRate;
     }
 
+    public void setCacheMode(String cacheMode) {
+        this.cacheMode = cacheMode;
+    }
+
+    public String getCacheMode() {
+        return cacheMode;
+    }
+
     public Long getChainSize() {
         return chainSize;
     }
diff --git a/api/src/com/cloud/offering/DiskOffering.java b/api/src/com/cloud/offering/DiskOffering.java
index fa6d66b..31bcfb3 100644
--- a/api/src/com/cloud/offering/DiskOffering.java
+++ b/api/src/com/cloud/offering/DiskOffering.java
@@ -35,6 +35,21 @@
 
     State getState();
 
+    public enum DiskCacheMode {
+        NONE("none"), WRITEBACK("writeback"), WRITETHROUGH("writethrough");
+        
+        private final String _diskCacheMode;
+
+        DiskCacheMode(String cacheMode) {
+            _diskCacheMode = cacheMode;
+        }
+
+        @Override
+        public String toString() {
+            return _diskCacheMode;
+        }
+    };
+
     String getUniqueName();
 
     boolean getUseLocalStorage();
@@ -92,4 +107,8 @@
     void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve);
 
     Integer getHypervisorSnapshotReserve();
+
+    DiskCacheMode getCacheMode();
+
+    void setCacheMode(DiskCacheMode cacheMode);
 }
diff --git a/api/src/com/cloud/vm/DiskProfile.java b/api/src/com/cloud/vm/DiskProfile.java
index 3d4c9e7..4412e3f 100644
--- a/api/src/com/cloud/vm/DiskProfile.java
+++ b/api/src/com/cloud/vm/DiskProfile.java
@@ -39,6 +39,7 @@
     private Long bytesWriteRate;
     private Long iopsReadRate;
     private Long iopsWriteRate;
+    private String cacheMode;
 
     private HypervisorType hyperType;
 
@@ -190,4 +191,12 @@
     public Long getIopsWriteRate() {
         return iopsWriteRate;
     }
+
+    public void setCacheMode(String cacheMode) {
+        this.cacheMode = cacheMode;
+    }
+
+    public String getCacheMode() {
+        return cacheMode;
+    }
 }
diff --git a/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java b/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java
index 4291d85..8649890 100644
--- a/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java
@@ -79,6 +79,9 @@
     @SerializedName("diskIopsWriteRate") @Param(description="io requests write rate of the disk offering")
     private Long iopsWriteRate;
 
+    @SerializedName("cacheMode") @Param(description="the cache mode to use for this disk offering. none, writeback or writethrough")
+    private String cacheMode;
+
     @SerializedName("displayoffering") @Param(description="whether to display the offering to the end user or not.")
     private Boolean displayOffering;
 
@@ -187,6 +190,14 @@
         this.maxIops = maxIops;
     }
 
+    public String getCacheMode() {
+        return this.cacheMode;
+    }
+
+    public void setCacheMode(String cacheMode) {
+        this.cacheMode = cacheMode;
+    }
+
     public String getStorageType() {
         return storageType;
     }
diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties
index 5885bd0..b870e7f 100644
--- a/client/WEB-INF/classes/resources/messages.properties
+++ b/client/WEB-INF/classes/resources/messages.properties
@@ -1096,6 +1096,7 @@
 label.storage.traffic=Storage Traffic
 label.storage.type=Storage Type
 label.qos.type=QoS Type
+label.cache.mode=Write-cache Type
 label.storage=Storage
 label.subdomain.access=Subdomain Access
 label.submit=Submit
diff --git a/core/src/com/cloud/agent/api/AttachVolumeCommand.java b/core/src/com/cloud/agent/api/AttachVolumeCommand.java
index e927619..e879897 100644
--- a/core/src/com/cloud/agent/api/AttachVolumeCommand.java
+++ b/core/src/com/cloud/agent/api/AttachVolumeCommand.java
@@ -40,6 +40,7 @@
     private Long bytesWriteRate;
     private Long iopsReadRate;
     private Long iopsWriteRate;
+    private String cacheMode;
 
     protected AttachVolumeCommand() {
     }
@@ -202,4 +203,12 @@
     public Long getIopsWriteRate() {
         return iopsWriteRate;
     }
+
+    public void setCacheMode(String cacheMode) {
+        this.cacheMode = cacheMode;
+    }
+
+    public String getCacheMode() {
+        return cacheMode;
+    }
 }
diff --git a/core/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java b/core/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java
index 46659a3..732fa7b 100644
--- a/core/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java
+++ b/core/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java
@@ -17,6 +17,7 @@
 package org.apache.cloudstack.storage.to;
 
 import com.cloud.hypervisor.Hypervisor;
+import com.cloud.offering.DiskOffering.DiskCacheMode;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
 
 import com.cloud.agent.api.to.DataObjectType;
@@ -44,6 +45,7 @@
     private Long bytesWriteRate;
     private Long iopsReadRate;
     private Long iopsWriteRate;
+    private DiskCacheMode cacheMode;
     private Hypervisor.HypervisorType hypervisorType;
 
     public VolumeObjectTO() {
@@ -71,6 +73,7 @@
         this.bytesWriteRate = volume.getBytesWriteRate();
         this.iopsReadRate = volume.getIopsReadRate();
         this.iopsWriteRate = volume.getIopsWriteRate();
+        this.cacheMode = volume.getCacheMode();
         this.hypervisorType = volume.getHypervisorType();
         setDeviceId(volume.getDeviceId());
     }
@@ -231,5 +234,12 @@
         this.deviceId = deviceId;
     }
 
+    public void setCacheMode(DiskCacheMode cacheMode) {
+        this.cacheMode = cacheMode;
+    }
+
+    public DiskCacheMode getCacheMode() {
+        return cacheMode;
+    }
 
 }
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java
index 3b4aba9..a7dbf79 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java
@@ -20,6 +20,7 @@
 
 import com.cloud.agent.api.Answer;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.offering.DiskOffering.DiskCacheMode;
 import com.cloud.storage.Volume;
 
 public interface VolumeInfo extends DataObject, Volume {
@@ -45,4 +46,6 @@
     Long getBytesWriteRate();
     Long getIopsReadRate();
     Long getIopsWriteRate();
+
+    DiskCacheMode getCacheMode();
 }
diff --git a/engine/schema/src/com/cloud/storage/DiskOfferingVO.java b/engine/schema/src/com/cloud/storage/DiskOfferingVO.java
index b5b3451..5960df4 100755
--- a/engine/schema/src/com/cloud/storage/DiskOfferingVO.java
+++ b/engine/schema/src/com/cloud/storage/DiskOfferingVO.java
@@ -24,8 +24,8 @@
 import javax.persistence.DiscriminatorColumn;
 import javax.persistence.DiscriminatorType;
 import javax.persistence.Entity;
-import javax.persistence.EnumType;
 import javax.persistence.Enumerated;
+import javax.persistence.EnumType;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
@@ -37,7 +37,9 @@
 import javax.persistence.Transient;
 
 import com.cloud.offering.DiskOffering;
+import com.cloud.offering.DiskOffering.DiskCacheMode;
 import com.cloud.utils.db.GenericDao;
+import org.apache.cloudstack.api.Identity;
 
 @Entity
 @Table(name = "disk_offering")
@@ -120,6 +122,10 @@
     @Column(name="iops_write_rate")
     Long iopsWriteRate;
 
+    @Column(name="cache_mode", updatable = true, nullable=false)
+    @Enumerated(value=EnumType.STRING)
+    private DiskCacheMode cacheMode;
+
     @Column(name="display_offering")
     boolean displayOffering = true;
 
@@ -135,6 +141,24 @@
     }
 
     public DiskOfferingVO(Long domainId, String name, String displayText, long diskSize, String tags, boolean isCustomized,
+            Boolean isCustomizedIops, Long minIops, Long maxIops, DiskCacheMode cacheMode) {
+        this.domainId = domainId;
+        this.name = name;
+        this.displayText = displayText;
+        this.diskSize = diskSize;
+        this.tags = tags;
+        this.recreatable = false;
+        this.type = Type.Disk;
+        this.useLocalStorage = false;
+        this.customized = isCustomized;
+        this.uuid = UUID.randomUUID().toString();
+        this.customizedIops = isCustomizedIops;
+        this.minIops = minIops;
+        this.maxIops = maxIops;
+        this.cacheMode = cacheMode;
+    }
+
+    public DiskOfferingVO(Long domainId, String name, String displayText, long diskSize, String tags, boolean isCustomized,
     		Boolean isCustomizedIops, Long minIops, Long maxIops) {
         this.domainId = domainId;
         this.name = name;
@@ -237,6 +261,16 @@
         this.maxIops = maxIops;
     }
 
+    @Override
+    public DiskCacheMode getCacheMode() {
+        return cacheMode;
+    }
+
+    @Override
+    public void setCacheMode(DiskCacheMode cacheMode) {
+        this.cacheMode = cacheMode;
+    }
+
 	@Override
     public String getUniqueName() {
         return uniqueName;
diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
index f761a0c..cec76c0 100644
--- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
+++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
@@ -39,6 +39,7 @@
 import com.cloud.agent.api.to.DataObjectType;
 import com.cloud.agent.api.to.DataTO;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.offering.DiskOffering.DiskCacheMode;
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.Volume;
@@ -214,6 +215,15 @@
         return null;
     }
 
+    @Override
+    public DiskCacheMode getCacheMode() {
+        DiskOfferingVO diskOfferingVO = getDiskOfferingVO();
+        if (diskOfferingVO != null) {
+            return diskOfferingVO.getCacheMode();
+        }
+        return null;
+    }
+
     public void update() {
         volumeDao.update(volumeVO.getId(), volumeVO);
         volumeVO = volumeDao.findById(volumeVO.getId());
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index 50561a9..0000bef 100755
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -193,6 +193,7 @@
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DevicesDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef.diskProtocol;
+import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef.diskCacheMode;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.FeaturesDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.FilesystemDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GraphicDef;
@@ -1510,6 +1511,7 @@
             volume.setBytesWriteRate(dskch.getBytesWriteRate());
             volume.setIopsReadRate(dskch.getIopsReadRate());
             volume.setIopsWriteRate(dskch.getIopsWriteRate());
+            volume.setCacheMode(dskch.getCacheMode());
             return new CreateAnswer(cmd, volume);
         } catch (CloudRuntimeException e) {
             s_logger.debug("Failed to create volume: " + e.toString());
@@ -2815,7 +2817,8 @@
                     cmd.getPoolUuid());
             KVMPhysicalDisk disk = primary.getPhysicalDisk(cmd.getVolumePath());
             attachOrDetachDisk(conn, cmd.getAttach(), cmd.getVmName(), disk,
-                    cmd.getDeviceId().intValue(), cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate());
+                    cmd.getDeviceId().intValue(), cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate(),
+                    cmd.getCacheMode());
         } catch (LibvirtException e) {
             return new AttachVolumeAnswer(cmd, e.toString());
         } catch (InternalErrorException e) {
@@ -3775,6 +3778,8 @@
                     disk.setIopsReadRate(volumeObjectTO.getIopsReadRate());
                 if ((volumeObjectTO.getIopsWriteRate() != null) && (volumeObjectTO.getIopsWriteRate() > 0))
                     disk.setIopsWriteRate(volumeObjectTO.getIopsWriteRate());
+                if (volumeObjectTO.getCacheMode() != null)
+                    disk.setCacheMode(DiskDef.diskCacheMode.valueOf(volumeObjectTO.getCacheMode().toString()));
             }
             vm.getDevices().addDevice(disk);
         }
@@ -3886,7 +3891,7 @@
 
     protected synchronized String attachOrDetachDisk(Connect conn,
             boolean attach, String vmName, KVMPhysicalDisk attachingDisk,
-            int devId, Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) throws LibvirtException, InternalErrorException {
+            int devId, Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate, String cacheMode) throws LibvirtException, InternalErrorException {
         List<DiskDef> disks = null;
         Domain dm = null;
         DiskDef diskdef = null;
@@ -3934,6 +3939,10 @@
                     diskdef.setIopsReadRate(iopsReadRate);
                 if ((iopsWriteRate != null) && (iopsWriteRate > 0))
                     diskdef.setIopsWriteRate(iopsWriteRate);
+
+                if (cacheMode != null) {
+                    diskdef.setCacheMode(DiskDef.diskCacheMode.valueOf(cacheMode));
+                }
             }
 
             String xml = diskdef.toString();
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
index a283768..d0b0b61 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
@@ -36,6 +36,7 @@
 
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef.diskProtocol;
+import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef.diskCacheMode;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.nicModel;
 
@@ -69,6 +70,7 @@
                 DiskDef def = new DiskDef();
                 if (type.equalsIgnoreCase("network")) {
                     String diskFmtType = getAttrValue("driver", "type", disk);
+                    String diskCacheMode = getAttrValue("driver", "cache", disk);
                     String diskPath = getAttrValue("source", "name", disk);
                     String protocol = getAttrValue("source", "protocol", disk);
                     String authUserName = getAttrValue("auth", "username", disk);
@@ -78,9 +80,12 @@
                     String diskLabel = getAttrValue("target", "dev", disk);
                     String bus = getAttrValue("target", "bus", disk);
                     def.defNetworkBasedDisk(diskPath, host, port, authUserName, poolUuid, diskLabel,
-                                            DiskDef.diskBus.valueOf(bus.toUpperCase()), DiskDef.diskProtocol.valueOf(protocol.toUpperCase()));
+                                            DiskDef.diskBus.valueOf(bus.toUpperCase()),
+                                            DiskDef.diskProtocol.valueOf(protocol.toUpperCase()));
+                    def.setCacheMode(DiskDef.diskCacheMode.valueOf(diskCacheMode));
                 } else {
                     String diskFmtType = getAttrValue("driver", "type", disk);
+                    String diskCacheMode = getAttrValue("driver", "cache", disk);
                     String diskFile = getAttrValue("source", "file", disk);
                     String diskDev = getAttrValue("source", "dev", disk);
 
@@ -103,6 +108,7 @@
                     } else if (type.equalsIgnoreCase("block")) {
                         def.defBlockBasedDisk(diskDev, diskLabel,
                                 DiskDef.diskBus.valueOf(bus.toUpperCase()));
+                        def.setCacheMode(DiskDef.diskCacheMode.valueOf(diskCacheMode));
                     }
                 }
 
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
index 6aaabc5..80dbb23 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
@@ -427,6 +427,23 @@
             }
         }
 
+        public enum diskCacheMode {
+            NONE("none"), WRITEBACK("writeback"), WRITETHROUGH("writethrough");
+            String _diskCacheMode;
+
+            diskCacheMode(String cacheMode) {
+                _diskCacheMode = cacheMode;
+            }
+
+            @Override
+            public String toString() {
+                if (_diskCacheMode == null) {
+                    return "none";
+                }
+                return _diskCacheMode;
+            }
+        }
+
         private deviceType _deviceType; /* floppy, disk, cdrom */
         private diskType _diskType;
         private diskProtocol _diskProtocol;
@@ -445,6 +462,7 @@
         private Long _bytesWriteRate;
         private Long _iopsReadRate;
         private Long _iopsWriteRate;
+        private diskCacheMode _diskCacheMode;
 
         public void setDeviceType(deviceType deviceType) {
             _deviceType = deviceType;
@@ -606,6 +624,10 @@
             _iopsWriteRate = iopsWriteRate;
         }
 
+        public void setCacheMode(diskCacheMode cacheMode) {
+            _diskCacheMode = cacheMode;
+        }
+
         @Override
         public String toString() {
             StringBuilder diskBuilder = new StringBuilder();
@@ -616,7 +638,7 @@
             diskBuilder.append(" type='" + _diskType + "'");
             diskBuilder.append(">\n");
             diskBuilder.append("<driver name='qemu'" + " type='" + _diskFmtType
-                    + "' cache='none' " + "/>\n");
+                    + "' cache='" + _diskCacheMode + "' " + "/>\n");
             if (_diskType == diskType.FILE) {
                 diskBuilder.append("<source ");
                 if (_sourcePath != null) {
diff --git a/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
index e29bb2b..fa620a7 100644
--- a/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
@@ -79,6 +79,7 @@
         diskOfferingResponse.setBytesWriteRate(offering.getBytesWriteRate());
         diskOfferingResponse.setIopsReadRate(offering.getIopsReadRate());
         diskOfferingResponse.setIopsWriteRate(offering.getIopsWriteRate());
+        diskOfferingResponse.setCacheMode(offering.getCacheMode());
         diskOfferingResponse.setObjectName("diskoffering");
 
         return diskOfferingResponse;
diff --git a/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java b/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java
index 9a679de..e925cd1 100644
--- a/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java
+++ b/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java
@@ -85,6 +85,9 @@
     @Column(name="iops_write_rate")
     Long iopsWriteRate;
 
+    @Column(name="cache_mode")
+    String cacheMode;
+
     @Column(name="type")
     Type type;
 
@@ -162,6 +165,18 @@
         return maxIops;
     }
 
+    public void setMaxIops(Long maxIops) {
+        this.maxIops = maxIops;
+    }
+
+    public String getCacheMode() {
+        return cacheMode;
+    }
+
+    public void setCacheMode(String cacheMode) {
+        this.cacheMode = cacheMode;
+    }
+
     public boolean isDisplayOffering() {
         return displayOffering;
     }
diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp
index 0ccfc23..45153d3 100644
--- a/ui/dictionary.jsp
+++ b/ui/dictionary.jsp
@@ -1078,6 +1078,7 @@
 'label.storage.tags': '<fmt:message key="label.storage.tags" />',
 'label.storage.type': '<fmt:message key="label.storage.type" />',
 'label.qos.type': '<fmt:message key="label.qos.type" />',
+'label.cache.mode': '<fmt:message key="label.cache.mode" />',
 'label.subdomain.access': '<fmt:message key="label.subdomain.access" />',
 'label.submit': '<fmt:message key="label.submit" />',
 'label.submitted.by': '<fmt:message key="label.submitted.by" />',
diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js
index 9c05169..8144466 100644
--- a/ui/scripts/configuration.js
+++ b/ui/scripts/configuration.js
@@ -1354,6 +1354,28 @@
                                             number: true
                                         }
                                     },
+                                    cacheMode: {
+                                        label: 'label.cache.mode',
+                                        docID: 'helpDiskOfferingCacheMode',
+                                        select: function(args) {
+                                            var items = [];
+                                            items.push({
+                                                id: 'none',
+                                                description: 'No disk cache'
+                                            });
+                                            items.push({
+                                                id: 'writeback',
+                                                description: 'Write-back disk caching'
+                                            });
+                                            items.push({
+                                                id: 'writethrough',
+                                                description: 'Write-through disk caching'
+                                            });
+                                            args.response.success({
+                                                data: items
+                                            });
+                                        }
+                                    },
                                     tags: {
                                         label: 'label.storage.tags',
                                         docID: 'helpDiskOfferingStorageTags'
@@ -1400,6 +1422,7 @@
                                     name: args.data.name,
                                     displaytext: args.data.description,
                                     storageType: args.data.storageType,
+                                    cacheMode: args.data.cacheMode,
                                     customized: (args.data.isCustomized == "on")
                                 };
 
@@ -1626,6 +1649,9 @@
                                     diskIopsWriteRate: {
                                         label: 'label.disk.iops.write.rate'
                                     },
+                                    cacheMode: {
+                                        label: 'label.cache.mode',
+                                    },
                                     tags: {
                                         label: 'label.storage.tags'
                                     },
diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js
index a3151b1..279c84c 100755
--- a/ui/scripts/docs.js
+++ b/ui/scripts/docs.js
@@ -328,6 +328,9 @@
     },
     helpDiskOfferingHypervisorSnapshotReserve: {
         desc: 'Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware) (Ex. The value 25 means 25%.)).',
+    },
+    helpDiskOfferingCacheMode: {
+        desc: 'The write caching mode to use for disks created with this disk offering. This can improve write performance.',
         externalLink: ''
     },
     helpDiskOfferingStorageTags: {