Repository: rook/rook
Pull request #13745 (2024-02-09 21:50:27)

CI: run canary tests nightly
Run the canary test suites with nightly CI so that we can detect
incoming issues with Ceph devel versions.

<!-- Thank you for contributing to Rook! -->

<!-- STEPS TO FOLLOW:
  1. Add a description of the changes (frequently the same as the commit description)
  2. Enter the issue number next to "Resolves #" below (if there is no tracking issue resolved, **remove that section**)
  3. Review our Contributing documentation at https://rook.io/docs/rook/latest/Contributing/development-flow/
  4. Follow the steps in the checklist below, starting with the **Commit Message Formatting**.
-->

<!-- Uncomment this section with the issue number if an issue is being resolved
**Issue resolved by this Pull Request:**
Resolves #
--->

**Checklist:**

- [ ] **Commit Message Formatting**: Commit titles and messages follow guidelines in the [developer guide](https://rook.io/docs/rook/latest/Contributing/development-flow/#commit-structure).
- [ ] Reviewed the developer guide on [Submitting a Pull Request](https://rook.io/docs/rook/latest/Contributing/development-flow/#submitting-a-pull-request)
- [ ] [Pending release notes](https://github.com/rook/rook/blob/master/PendingReleaseNotes.md) updated with breaking and/or notable changes for the next minor release.
- [ ] Documentation has been updated, if necessary.
- [ ] Unit tests have been added, if necessary.
- [ ] Integration tests have been added, if necessary.



Patch:
diff --git a/.github/workflows/daily-nightly-jobs.yml b/.github/workflows/daily-nightly-jobs.yml
index e361880f2..b5e28acb5 100644
--- a/.github/workflows/daily-nightly-jobs.yml
+++ b/.github/workflows/daily-nightly-jobs.yml
@@ -2,6 +2,7 @@ name: Daily nightly jobs
 on:
   schedule:
     - cron: "0 0 * * *" # every day at midnight
+  workflow_dispatch: {}
 
 defaults:
   run:
@@ -394,64 +395,18 @@ jobs:
           name: ceph-upgrade-suite-quincy-artifact
           path: /home/runner/work/rook/rook/tests/integration/_output/tests/
 
-  rgw-multisite-test-with-ceph-devel:
-    if: github.repository == 'rook/rook'
-    runs-on: ubuntu-20.04
+  # run default canary suite
+  canary-tests:
+    uses: ./.github/workflows/canary-integration-test.yml
+    secrets: inherit
+
+  # run canary suite with relevant ceph devel versions
+  canary-tests-devel:
+    uses: ./.github/workflows/canary-integration-test.yml
     strategy:
       matrix:
         ceph-image-tag:
           ["latest-main-devel", "latest-quincy-devel", "latest-reef-devel"]
-
-    steps:
-      - uses: actions/checkout@v4
-        with:
-          fetch-depth: 0
-
-      - name: consider debugging
-        uses: ./.github/workflows/tmate_debug
-        with:
-          use-tmate: ${{ secrets.USE_TMATE }}
-
-      - name: run RGW multisite test
-        uses: ./.github/workflows/rgw-multisite-test
-        with:
-          github-token: ${{ secrets.GITHUB_TOKEN }}
-          ceph-image: quay.io/ceph/daemon-base:${{ matrix.ceph-image-tag }}
-
-      - name: upload test result
-        uses: actions/upload-artifact@v4
-        if: always()
-        with:
-          name: rgw-multisite-testing-ceph-${{ matrix.ceph-image-tag }}
-          path: test
-
-  encryption-pvc-kms-ibm-kp:
-    if: github.repository == 'rook/rook'
-    runs-on: ubuntu-20.04
-    steps:
-      - uses: actions/checkout@v4
-        with:
-          fetch-depth: 0
-
-      - name: consider debugging
-        uses: ./.github/workflows/tmate_debug
-        with:
-          use-tmate: ${{ secrets.USE_TMATE }}
-
-      - name: run encryption KMS IBM Key Protect
-        uses: ./.github/workflows/encryption-pvc-kms-ibm-kp
-        if: "env.IBM_KP_SERVICE_INSTANCE_ID != '' && env.IBM_KP_SERVICE_API_KEY != ''"
-        env:
-          IBM_KP_SERVICE_INSTANCE_ID: ${{ secrets.IBM_INSTANCE_ID }}
-          IBM_KP_SERVICE_API_KEY: ${{ secrets.IBM_SERVICE_API_KEY }}
-        with:
-          ibm-instance-id: ${{ secrets.IBM_INSTANCE_ID }}
-          ibm-service-api-key: ${{ secrets.IBM_SERVICE_API_KEY }}
-          github-token: ${{ secrets.GITHUB_TOKEN }}
-
-      - name: upload test result
-        uses: actions/upload-artifact@v4
-        if: always()
-        with:
-          name: encryption-pvc-kms-ibm-kp
-          path: test
+    with:
+      ceph-image: quay.io/ceph/daemon-base:${{ matrix.ceph-image-tag }}
+    secrets: inherit
diff --git a/Documentation/CRDs/Cluster/ceph-cluster-crd.md b/Documentation/CRDs/Cluster/ceph-cluster-crd.md
index c88e09478..865c9278b 100755
--- a/Documentation/CRDs/Cluster/ceph-cluster-crd.md
+++ b/Documentation/CRDs/Cluster/ceph-cluster-crd.md
@@ -32,7 +32,8 @@ Settings can be specified at the global level to apply to the cluster as a whole
   Tags also exist that would give the latest version, but they are only recommended for test environments. For example, the tag `v17` will be updated each time a new Quincy build is released.
   Using the `v17` tag is not recommended in production because it may lead to inconsistent versions of the image running across different nodes in the cluster.
     * `allowUnsupported`: If `true`, allow an unsupported major version of the Ceph release. Currently `quincy` and `reef` are supported. Future versions such as `squid` (v19) would require this to be set to `true`. Should be set to `false` in production.
-    * `imagePullPolicy`: The image pull policy for the ceph daemon pods. Possible values are `Always`, `IfNotPresent`, and `Never`. The default is `IfNotPresent`.
+  `imagePullPolicy`: The image pull policy for the ceph daemon pods. Possible values are `Always`, `IfNotPresent`, and `Never`.
+  The default is `IfNotPresent`.
 * `dataDirHostPath`: The path on the host ([hostPath](https://kubernetes.io/docs/concepts/storage/volumes/#hostpath)) where config and data should be stored for each of the services. If the directory does not exist, it will be created. Because this directory persists on the host, it will remain after pods are deleted. Following paths and any of their subpaths **must not be used**: `/etc/ceph`, `/rook` or `/var/log/ceph`.
     * **WARNING**: For test scenarios, if you delete a cluster and start a new cluster on the same hosts, the path used by `dataDirHostPath` must be deleted. Otherwise, stale keys and other config will remain from the previous cluster and the new mons will fail to start.
 If this value is empty, each pod will get an ephemeral directory to store their config files that is tied to the lifetime of the pod running on that node. More details can be found in the Kubernetes [empty dir docs](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir).
@@ -477,7 +478,7 @@ See the table in [OSD Configuration Settings](#osd-configuration-settings) to kn
 
 The following storage selection settings are specific to Ceph and do not apply to other backends. All variables are key-value pairs represented as strings.
 
-* `metadataDevice`: Name of a device, [partition](#limitations-of-metadata-device) or lvm to use for the metadata of OSDs on each node.  Performance can be improved by using a low latency device (such as SSD or NVMe) as the metadata device, while other spinning platter (HDD) devices on a node are used to store data. Provisioning will fail if the user specifies a `metadataDevice` but that device is not used as a metadata device by Ceph. Notably, `ceph-volume` will not use a device of the same device class (HDD, SSD, NVMe) as OSD devices for metadata, resulting in this failure.
+* `metadataDevice`: Name of a device or lvm to use for the metadata of OSDs on each node.  Performance can be improved by using a low latency device (such as SSD or NVMe) as the metadata device, while other spinning platter (HDD) devices on a node are used to store data. Provisioning will fail if the user specifies a `metadataDevice` but that device is not used as a metadata device by Ceph. Notably, `ceph-volume` will not use a device of the same device class (HDD, SSD, NVMe) as OSD devices for metadata, resulting in this failure.
 * `databaseSizeMB`:  The size in MB of a bluestore database. Include quotes around the size.
 * `walSizeMB`:  The size in MB of a bluestore write ahead log (WAL). Include quotes around the size.
 * `deviceClass`: The [CRUSH device class](https://ceph.io/community/new-luminous-crush-device-classes/) to use for this selection of storage devices. (By default, if a device's class has not already been set, OSDs will automatically set a device's class to either `hdd`, `ssd`, or `nvme`  based on the hardware properties exposed by the Linux kernel.) These storage classes can then be used to select the devices backing a storage pool by specifying them as the value of [the pool spec's `deviceClass` field](../Block-Storage/ceph-block-pool-crd.md#spec).
@@ -497,10 +498,6 @@ Allowed configurations are:
 | crypt             |                                                                                                   |                                                                                 |
 | mpath             |                                                                                                   |                                                                                 |
 
-#### Limitations of metadata device
-- If `metadataDevice` is specified in the global OSD configuration or in the node level OSD configuration, the metadata device will be shared between all OSDs on the same node. In other words, OSDs will be initialized by `lvm batch`. In this case, we can't use partition device.
-- If `metadataDevice` is specified in the device local configuration, we can use partition as metadata device. In other words, OSDs are initialized by `lvm prepare`.
-
 ### Annotations and Labels
 
 Annotations and Labels can be specified so that the Rook components will have those annotations / labels added to them.
@@ -671,6 +668,7 @@ spec:
     - name: "172.17.4.201"
       resources:
         limits:
+          cpu: "2"
           memory: "4096Mi"
         requests:
           cpu: "2"
diff --git a/Documentation/CRDs/Cluster/pvc-cluster.md b/Documentation/CRDs/Cluster/pvc-cluster.md
index 25cb86d36..11c1d6103 100644
--- a/Documentation/CRDs/Cluster/pvc-cluster.md
+++ b/Documentation/CRDs/Cluster/pvc-cluster.md
@@ -85,6 +85,7 @@ spec:
       portable: false
       resources:
         limits:
+          cpu: "500m"
           memory: "4Gi"
         requests:
           cpu: "500m"
diff --git a/Documentation/CRDs/Object-Storage/ceph-object-store-crd.md b/Documentation/CRDs/Object-Storage/ceph-object-store-crd.md
index 37dc1b2da..0b6820480 100644
--- a/Documentation/CRDs/Object-Storage/ceph-object-store-crd.md
+++ b/Documentation/CRDs/Object-Storage/ceph-object-store-crd.md
@@ -58,6 +58,7 @@ spec:
     #  topologySpreadConstraints:
     resources:
     #  limits:
+    #    cpu: "500m"
     #    memory: "1024Mi"
     #  requests:
     #    cpu: "500m"
diff --git a/Documentation/CRDs/Shared-Filesystem/ceph-filesystem-crd.md b/Documentation/CRDs/Shared-Filesystem/ceph-filesystem-crd.md
index 4f8fcdb38..95f89b303 100644
--- a/Documentation/CRDs/Shared-Filesystem/ceph-filesystem-crd.md
+++ b/Documentation/CRDs/Shared-Filesystem/ceph-filesystem-crd.md
@@ -55,6 +55,7 @@ spec:
     #  topologySpreadConstraints:
     resources:
     #  limits:
+    #    cpu: "500m"
     #    memory: "1024Mi"
     #  requests:
     #    cpu: "500m"
diff --git a/Documentation/CRDs/ceph-nfs-crd.md b/Documentation/CRDs/ceph-nfs-crd.md
index 80b8465f6..23df9eb20 100644
--- a/Documentation/CRDs/ceph-nfs-crd.md
+++ b/Documentation/CRDs/ceph-nfs-crd.md
@@ -42,6 +42,7 @@ spec:
 
     resources:
       limits:
+        cpu: "3"
         memory: "8Gi"
       requests:
         cpu: "3"
diff --git a/Documentation/Helm-Charts/ceph-cluster-chart.md b/Documentation/Helm-Charts/ceph-cluster-chart.md
index 96815d000..0f17dbd81 100644
--- a/Documentation/Helm-Charts/ceph-cluster-chart.md
+++ b/Documentation/Helm-Charts/ceph-cluster-chart.md
@@ -84,7 +84,7 @@ The following table lists the configurable parameters of the rook-operator chart
 | `toolbox.enabled` | Enable Ceph debugging pod deployment. See [toolbox](../Troubleshooting/ceph-toolbox.md) | `false` |
 | `toolbox.image` | Toolbox image, defaults to the image used by the Ceph cluster | `nil` |
 | `toolbox.priorityClassName` | Set the priority class for the toolbox if desired | `nil` |
-| `toolbox.resources` | Toolbox resources | `{"limits":{"memory":"1Gi"},"requests":{"cpu":"100m","memory":"128Mi"}}` |
+| `toolbox.resources` | Toolbox resources | `{"limits":{"cpu":"500m","memory":"1Gi"},"requests":{"cpu":"100m","memory":"128Mi"}}` |
 | `toolbox.tolerations` | Toolbox tolerations | `[]` |
 
 ### **Ceph Cluster Spec**
diff --git a/Documentation/Helm-Charts/operator-chart.md b/Documentation/Helm-Charts/operator-chart.md
index d0b7d43af..c9f9439f8 100644
--- a/Documentation/Helm-Charts/operator-chart.md
+++ b/Documentation/Helm-Charts/operator-chart.md
@@ -59,7 +59,7 @@ The following table lists the configurable parameters of the rook-operator chart
 | `csi.cephFSKernelMountOptions` | Set CephFS Kernel mount options to use https://docs.ceph.com/en/latest/man/8/mount.ceph/#options. Set to "ms_mode=secure" when connections.encrypted is enabled in CephCluster CR | `nil` |
 | `csi.cephFSPluginUpdateStrategy` | CSI CephFS plugin daemonset update strategy, supported values are OnDelete and RollingUpdate | `RollingUpdate` |
 | `csi.cephFSPluginUpdateStrategyMaxUnavailable` | A maxUnavailable parameter of CSI cephFS plugin daemonset update strategy. | `1` |
-| `csi.cephcsi.image` | Ceph CSI image | `quay.io/cephcsi/cephcsi:v3.10.2` |
+| `csi.cephcsi.image` | Ceph CSI image | `quay.io/cephcsi/cephcsi:v3.10.1` |
 | `csi.cephfsLivenessMetricsPort` | CSI CephFS driver metrics port | `9081` |
 | `csi.cephfsPodLabels` | Labels to add to the CSI CephFS Deployments and DaemonSets Pods | `nil` |
 | `csi.clusterName` | Cluster name identifier to set as metadata on the CephFS subvolume and RBD images. This will be useful in cases like for example, when two container orchestrator clusters (Kubernetes/OCP) are using a single ceph cluster | `nil` |
@@ -115,6 +115,8 @@ The following table lists the configurable parameters of the rook-operator chart
 | `csi.rbdPluginUpdateStrategy` | CSI RBD plugin daemonset update strategy, supported values are OnDelete and RollingUpdate | `RollingUpdate` |
 | `csi.rbdPluginUpdateStrategyMaxUnavailable` | A maxUnavailable parameter of CSI RBD plugin daemonset update strategy. | `1` |
 | `csi.rbdPodLabels` | Labels to add to the CSI RBD Deployments and DaemonSets Pods | `nil` |
+| `csi.readAffinity.crushLocationLabels` | Define which node labels to use as CRUSH location. This should correspond to the values set in the CRUSH map. | labels listed [here](../CRDs/Cluster/ceph-cluster-crd.md#osd-topology) |
+| `csi.readAffinity.enabled` | Enable read affinity for RBD volumes. Recommended to set to true if running kernel 5.8 or newer. | `false` |
 | `csi.registrar.image` | Kubernetes CSI registrar image | `registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.9.1` |
 | `csi.resizer.image` | Kubernetes CSI resizer image | `registry.k8s.io/sig-storage/csi-resizer:v1.9.2` |
 | `csi.serviceMonitor.enabled` | Enable ServiceMonitor for Ceph CSI drivers | `false` |
@@ -149,7 +151,7 @@ The following table lists the configurable parameters of the rook-operator chart
 | `pspEnable` | If true, create & use PSP resources | `false` |
 | `rbacAggregate.enableOBCs` | If true, create a ClusterRole aggregated to [user facing roles](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles) for objectbucketclaims | `false` |
 | `rbacEnable` | If true, create & use RBAC resources | `true` |
-| `resources` | Pod resource requests & limits | `{"limits":{"memory":"512Mi"},"requests":{"cpu":"200m","memory":"128Mi"}}` |
+| `resources` | Pod resource requests & limits | `{"limits":{"cpu":"1500m","memory":"512Mi"},"requests":{"cpu":"200m","memory":"128Mi"}}` |
 | `scaleDownOperator` | If true, scale down the rook operator. This is useful for administrative actions where the rook operator must be scaled down, while using gitops style tooling to deploy your helm charts. | `false` |
 | `tolerations` | List of Kubernetes [`tolerations`](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to add to the Deployment. | `[]` |
 | `unreachableNodeTolerationSeconds` | Delay to use for the `node.kubernetes.io/unreachable` pod failure toleration to override the Kubernetes default of 5 minutes | `5` |
diff --git a/Documentation/Storage-Configuration/Ceph-CSI/custom-images.md b/Documentation/Storage-Configuration/Ceph-CSI/custom-images.md
index 703f85b45..67bc50084 100644
--- a/Documentation/Storage-Configuration/Ceph-CSI/custom-images.md
+++ b/Documentation/Storage-Configuration/Ceph-CSI/custom-images.md
@@ -18,7 +18,7 @@ kubectl -n $ROOK_OPERATOR_NAMESPACE edit configmap rook-ceph-operator-config
 The default upstream images are included below, which you can change to your desired images.
 
 ```yaml
-ROOK_CSI_CEPH_IMAGE: "quay.io/cephcsi/cephcsi:v3.10.2"
+ROOK_CSI_CEPH_IMAGE: "quay.io/cephcsi/cephcsi:v3.10.1"
 ROOK_CSI_REGISTRAR_IMAGE: "registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.9.1"
 ROOK_CSI_PROVISIONER_IMAGE: "registry.k8s.io/sig-storage/csi-provisioner:v3.6.3"
 ROOK_CSI_ATTACHER_IMAGE: "registry.k8s.io/sig-storage/csi-attacher:v4.4.2"
@@ -32,7 +32,7 @@ ROOK_CSIADDONS_IMAGE: "quay.io/csiaddons/k8s-sidecar:v0.8.0"
 If image version is not passed along with the image name in any of the variables above,
 Rook will add the corresponding default version to that image.
 Example: if `ROOK_CSI_CEPH_IMAGE: "quay.io/private-repo/cephcsi"` is passed,
-Rook will add internal default version and consume it as `"quay.io/private-repo/cephcsi:v3.10.2"`.
+Rook will add internal default version and consume it as `"quay.io/private-repo/cephcsi:v3.10.1"`.
 
 ### **Use default images**
 
diff --git a/Documentation/Storage-Configuration/Shared-Filesystem-CephFS/filesystem-storage.md b/Documentation/Storage-Configuration/Shared-Filesystem-CephFS/filesystem-storage.md
index a8e82bd37..496275c99 100644
--- a/Documentation/Storage-Configuration/Shared-Filesystem-CephFS/filesystem-storage.md
+++ b/Documentation/Storage-Configuration/Shared-Filesystem-CephFS/filesystem-storage.md
@@ -168,6 +168,7 @@ spec:
         imagePullPolicy: Always
         resources:
           limits:
+            cpu: 100m
             memory: 100Mi
         env:
         # Configuration reference: https://docs.docker.com/registry/configuration/
diff --git a/PendingReleaseNotes.md b/PendingReleaseNotes.md
index 609e93295..0fa402039 100644
--- a/PendingReleaseNotes.md
+++ b/PendingReleaseNotes.md
@@ -1,7 +1,6 @@
 # v1.14 Pending Release Notes
 
 ## Breaking Changes
-- The removal of `CSI_ENABLE_READ_AFFINITY` option and its replacement with per-cluster
-read affinity setting in cephCluster CR (CSIDriverOptions section) in [PR](https://github.com/rook/rook/pull/13665)
+
 
 ## Features
diff --git a/deploy/charts/rook-ceph-cluster/templates/cephecblockpool.yaml b/deploy/charts/rook-ceph-cluster/templates/cephecblockpool.yaml
index 010e864f9..85581f5cd 100644
--- a/deploy/charts/rook-ceph-cluster/templates/cephecblockpool.yaml
+++ b/deploy/charts/rook-ceph-cluster/templates/cephecblockpool.yaml
@@ -7,36 +7,26 @@ metadata:
   name: {{ $ecblockpool.name }}
   namespace: {{ $root.Release.Namespace }} # namespace:cluster
 spec:
-{{ toYaml $ecblockpool.spec.dataPool | indent 2 }}
----
-apiVersion: ceph.rook.io/v1
-kind: CephBlockPool
-metadata:
-  name: {{ $ecblockpool.name }}-metadata
-  namespace: {{ $root.Release.Namespace }} # namespace:cluster
-spec:
-{{ toYaml $ecblockpool.spec.metadataPool | indent 2 }}
+{{ toYaml $ecblockpool.spec | indent 2 }}
+{{ end }}
+{{- if .Values.cephECStorageClass }}
+{{ $cephEcStorage :=  .Values.cephECStorageClass}}
 ---
-{{- if default false $ecblockpool.storageClass.enabled }}
 apiVersion: storage.k8s.io/v1
 kind: StorageClass
 metadata:
-  name: {{ $ecblockpool.storageClass.name }}
-  {{- if $ecblockpool.storageClass.isDefault }}
-  annotations:
-    storageclass.kubernetes.io/is-default-class: "true"
-  {{end}}
+  name: {{ $cephEcStorage.name }}
 {{- if $root.Values.csiDriverNamePrefix }}
 provisioner: {{ $root.Values.csiDriverNamePrefix }}.rbd.csi.ceph.com
 {{- else }}
 provisioner: {{ $root.Values.operatorNamespace }}.rbd.csi.ceph.com
 {{- end }}
 parameters:
-  clusterID: {{ $ecblockpool.parameters.clusterID }}
-  dataPool: {{ $ecblockpool.name }}-metadata
-  pool: {{ $ecblockpool.name }}
-  imageFormat: "{{ $ecblockpool.parameters.imageFormat }}"
-  imageFeatures: {{ $ecblockpool.parameters.imageFeatures }}
+  clusterID: {{ $cephEcStorage.parameters.clusterID }}
+  dataPool: {{ $cephEcStorage.parameters.dataPool }}
+  pool: {{ $cephEcStorage.parameters.pool }}
+  imageFormat: "{{ $cephEcStorage.parameters.imageFormat }}"
+  imageFeatures: {{ $cephEcStorage.parameters.imageFeatures }}
 
   # The secrets contain Ceph admin credentials. These are generated automatically by the operator
   # in the same namespace as the cluster.
@@ -55,7 +45,6 @@ parameters:
 # the PVC to application pod if nodeplugin pod restart.
 # Its still in Alpha support. Therefore, this option is not recommended for production use.
 #mounter: rbd-nbd
-allowVolumeExpansion: {{ $ecblockpool.storageClass.allowVolumeExpansion }}
-reclaimPolicy: {{ $ecblockpool.storageClass.reclaimPolicy }}
+allowVolumeExpansion: {{ $cephEcStorage.allowVolumeExpansion }}
+reclaimPolicy: {{ $cephEcStorage.reclaimPolicy }}
 {{ end }}
-{{ end }}
\ No newline at end of file
diff --git a/deploy/charts/rook-ceph-cluster/values.yaml b/deploy/charts/rook-ceph-cluster/values.yaml
index 534858b52..3953b65b4 100644
--- a/deploy/charts/rook-ceph-cluster/values.yaml
+++ b/deploy/charts/rook-ceph-cluster/values.yaml
@@ -40,6 +40,7 @@ toolbox:
   # -- Toolbox resources
   resources:
     limits:
+      cpu: "500m"
       memory: "1Gi"
     requests:
       cpu: "100m"
@@ -280,18 +281,21 @@ cephClusterSpec:
   resources:
     mgr:
       limits:
+        cpu: "1000m"
         memory: "1Gi"
       requests:
         cpu: "500m"
         memory: "512Mi"
     mon:
       limits:
+        cpu: "2000m"
         memory: "2Gi"
       requests:
         cpu: "1000m"
         memory: "1Gi"
     osd:
       limits:
+        cpu: "2000m"
         memory: "4Gi"
       requests:
         cpu: "1000m"
@@ -310,30 +314,35 @@ cephClusterSpec:
         memory: "50Mi"
     mgr-sidecar:
       limits:
+        cpu: "500m"
         memory: "100Mi"
       requests:
         cpu: "100m"
         memory: "40Mi"
     crashcollector:
       limits:
+        cpu: "500m"
         memory: "60Mi"
       requests:
         cpu: "100m"
         memory: "60Mi"
     logcollector:
       limits:
+        cpu: "500m"
         memory: "1Gi"
       requests:
         cpu: "100m"
         memory: "100Mi"
     cleanup:
       limits:
+        cpu: "500m"
         memory: "1Gi"
       requests:
         cpu: "500m"
         memory: "100Mi"
     exporter:
       limits:
+        cpu: "250m"
         memory: "128Mi"
       requests:
         cpu: "50m"
@@ -513,6 +522,7 @@ cephFileSystems:
         activeStandby: true
         resources:
           limits:
+            cpu: "2000m"
             memory: "4Gi"
           requests:
             cpu: "1000m"
@@ -586,6 +596,7 @@ cephObjectStores:
         port: 80
         resources:
           limits:
+            cpu: "2000m"
             memory: "2Gi"
           requests:
             cpu: "1000m"
@@ -615,57 +626,65 @@ cephObjectStores:
       #     - objectstore.example.com
       #   secretName: ceph-objectstore-tls
       # ingressClassName: nginx
-## cephECBlockPools are disabled by default, please remove the comments and set desired values to enable it
-## For erasure coded a replicated metadata pool is required.
-## https://rook.io/docs/rook/latest/CRDs/Shared-Filesystem/ceph-filesystem-crd/#erasure-coded
+# cephECBlockPools are disabled by default, please remove the comments and set desired values to enable it
 #cephECBlockPools:
-#  - name: ec-pool
+#  # For erasure coded a replicated metadata pool is required.
+#  # https://rook.io/docs/rook/latest/CRDs/Shared-Filesystem/ceph-filesystem-crd/#erasure-coded
+#  - name: ec-metadata-pool
+#    # see https://github.com/rook/rook/blob/master/Documentation/CRDs/Block-Storage/ceph-block-pool-crd.md#spec for available configuration
+#    spec:
+#      replicated:
+#        size: 2
+#  - name: ec-data-pool
 #    spec:
-#      metadataPool:
-#        replicated:
-#          size: 2
-#      dataPool:
-#        failureDomain: osd
-#        erasureCoded:
-#          dataChunks: 2
-#          codingChunks: 1
-#        deviceClass: hdd
+#      failureDomain: osd
+#      erasureCoded:
+#        dataChunks: 2
+#        codingChunks: 1
+#      deviceClass: hdd
+
+# cephECStorageClass also is disabled by default, please remove the comments and set desired values to enable it
+# if cephECBlockPools are uncommented you must remove the comments of cephEcStorageClass as well
+#cephECStorageClass:
+#  name: rook-ceph-block
+#  parameters:
+#    # clusterID is the namespace where the rook cluster is running
+#    # If you change this namespace, also change the namespace below where the secret namespaces are defined
+#    clusterID: rook-ceph # namespace:cluster
 #
-#    parameters:
-#      # clusterID is the namespace where the rook cluster is running
-#      # If you change this namespace, also change the namespace below where the secret namespaces are defined
-#      clusterID: rook-ceph # namespace:cluster
-#      # (optional) mapOptions is a comma-separated list of map options.
-#      # For krbd options refer
-#      # https://docs.ceph.com/docs/master/man/8/rbd/#kernel-rbd-krbd-options
-#      # For nbd options refer
-#      # https://docs.ceph.com/docs/master/man/8/rbd-nbd/#options
-#      # mapOptions: lock_on_read,queue_depth=1024
+#    # If you want to use erasure coded pool with RBD, you need to create
+#    # two pools. one erasure coded and one replicated.
+#    # You need to specify the replicated pool here in the `pool` parameter, it is
+#    # used for the metadata of the images.
+#    # The erasure coded pool must be set as the `dataPool` parameter below.
+#    dataPool: ec-data-pool
+#    pool: ec-metadata-pool
 #
-#      # (optional) unmapOptions is a comma-separated list of unmap options.
-#      # For krbd options refer
-#      # https://docs.ceph.com/docs/master/man/8/rbd/#kernel-rbd-krbd-options
-#      # For nbd options refer
-#      # https://docs.ceph.com/docs/master/man/8/rbd-nbd/#options
-#      # unmapOptions: force
+#    # (optional) mapOptions is a comma-separated list of map options.
+#    # For krbd options refer
+#    # https://docs.ceph.com/docs/master/man/8/rbd/#kernel-rbd-krbd-options
+#    # For nbd options refer
+#    # https://docs.ceph.com/docs/master/man/8/rbd-nbd/#options
+#    # mapOptions: lock_on_read,queue_depth=1024
 #
-#      # RBD image format. Defaults to "2".
-#      imageFormat: "2"
+#    # (optional) unmapOptions is a comma-separated list of unmap options.
+#    # For krbd options refer
+#    # https://docs.ceph.com/docs/master/man/8/rbd/#kernel-rbd-krbd-options
+#    # For nbd options refer
+#    # https://docs.ceph.com/docs/master/man/8/rbd-nbd/#options
+#    # unmapOptions: force
 #
-#      # RBD image features, equivalent to OR'd bitfield value: 63
-#      # Available for imageFormat: "2". Older releases of CSI RBD
-#      # support only the `layering` feature. The Linux kernel (KRBD) supports the
-#      # full feature complement as of 5.4
-#      # imageFeatures: layering,fast-diff,object-map,deep-flatten,exclusive-lock
-#      imageFeatures: layering
+#    # RBD image format. Defaults to "2".
+#    imageFormat: "2"
 #
-#    storageClass:
-#      provisioner: rook-ceph.rbd.csi.ceph.com # csi-provisioner-name
-#      enabled: true
-#      name:  rook-ceph-block
-#      isDefault: false
-#      allowVolumeExpansion: true
-#      reclaimPolicy: Delete
+#    # RBD image features, equivalent to OR'd bitfield value: 63
+#    # Available for imageFormat: "2". Older releases of CSI RBD
+#    # support only the `layering` feature. The Linux kernel (KRBD) supports the
+#    # full feature complement as of 5.4
+#    # imageFeatures: layering,fast-diff,object-map,deep-flatten,exclusive-lock
+#    imageFeatures: layering
+#  allowVolumeExpansion: true
+#  reclaimPolicy: Delete
 
 # -- CSI driver name prefix for cephfs, rbd and nfs.
 # @default -- `namespace name where rook-ceph operator is deployed`
diff --git a/deploy/charts/rook-ceph/templates/configmap.yaml b/deploy/charts/rook-ceph/templates/configmap.yaml
index d6af5cc79..ee7e48142 100644
--- a/deploy/charts/rook-ceph/templates/configmap.yaml
+++ b/deploy/charts/rook-ceph/templates/configmap.yaml
@@ -118,6 +118,12 @@ data:
   CSI_TOPOLOGY_DOMAIN_LABELS: {{ .Values.csi.topology.domainLabels | join "," }}
 {{- end }}
 {{- end }}
+{{- if .Values.csi.readAffinity }}
+  CSI_ENABLE_READ_AFFINITY: {{ .Values.csi.readAffinity.enabled | quote }}
+{{- if .Values.csi.readAffinity.crushLocationLabels }}
+  CSI_CRUSH_LOCATION_LABELS: {{ .Values.csi.readAffinity.crushLocationLabels | join "," }}
+{{- end }}
+{{- end }}
 {{- if .Values.csi.nfs }}
   ROOK_CSI_ENABLE_NFS: {{ .Values.csi.nfs.enabled | quote }}
 {{- end }}
diff --git a/deploy/charts/rook-ceph/values.yaml b/deploy/charts/rook-ceph/values.yaml
index 0781cf3b6..4ca32198b 100644
--- a/deploy/charts/rook-ceph/values.yaml
+++ b/deploy/charts/rook-ceph/values.yaml
@@ -23,6 +23,7 @@ crds:
 # -- Pod resource requests & limits
 resources:
   limits:
+    cpu: 1500m
     memory: 512Mi
   requests:
     cpu: 200m
@@ -217,6 +218,7 @@ csi:
           cpu: 100m
         limits:
           memory: 256Mi
+          cpu: 200m
     - name : csi-resizer
       resource:
         requests:
@@ -224,6 +226,7 @@ csi:
           cpu: 100m
         limits:
           memory: 256Mi
+          cpu: 200m
     - name : csi-attacher
       resource:
         requests:
@@ -231,6 +234,7 @@ csi:
           cpu: 100m
         limits:
           memory: 256Mi
+          cpu: 200m
     - name : csi-snapshotter
       resource:
         requests:
@@ -238,12 +242,15 @@ csi:
           cpu: 100m
         limits:
           memory: 256Mi
+          cpu: 200m
     - name : csi-rbdplugin
       resource:
         requests:
           memory: 512Mi
+          cpu: 250m
         limits:
           memory: 1Gi
+          cpu: 500m
     - name : csi-omap-generator
       resource:
         requests:
@@ -251,6 +258,7 @@ csi:
           cpu: 250m
         limits:
           memory: 1Gi
+          cpu: 500m
     - name : liveness-prometheus
       resource:
         requests:
@@ -258,6 +266,7 @@ csi:
           cpu: 50m
         limits:
           memory: 256Mi
+          cpu: 100m
 
   # -- CEPH CSI RBD plugin resource requirement list
   # @default -- see values.yaml
@@ -269,6 +278,7 @@ csi:
           cpu: 50m
         limits:
           memory: 256Mi
+          cpu: 100m
     - name : csi-rbdplugin
       resource:
         requests:
@@ -276,6 +286,7 @@ csi:
           cpu: 250m
         limits:
           memory: 1Gi
+          cpu: 500m
     - name : liveness-prometheus
       resource:
         requests:
@@ -283,6 +294,7 @@ csi:
           cpu: 50m
         limits:
           memory: 256Mi
+          cpu: 100m
 
   # -- CEPH CSI CephFS provisioner resource requirement list
   # @default -- see values.yaml
@@ -294,6 +306,7 @@ csi:
           cpu: 100m
         limits:
           memory: 256Mi
+          cpu: 200m
     - name : csi-resizer
       resource:
         requests:
@@ -301,6 +314,7 @@ csi:
           cpu: 100m
         limits:
           memory: 256Mi
+          cpu: 200m
     - name : csi-attacher
       resource:
         requests:
@@ -308,6 +322,7 @@ csi:
           cpu: 100m
         limits:
           memory: 256Mi
+          cpu: 200m
     - name : csi-snapshotter
       resource:
         requests:
@@ -315,6 +330,7 @@ csi:
           cpu: 100m
         limits:
           memory: 256Mi
+          cpu: 200m
     - name : csi-cephfsplugin
       resource:
         requests:
@@ -322,6 +338,7 @@ csi:
           cpu: 250m
         limits:
           memory: 1Gi
+          cpu: 500m
     - name : liveness-prometheus
       resource:
         requests:
@@ -329,6 +346,7 @@ csi:
           cpu: 50m
         limits:
           memory: 256Mi
+          cpu: 100m
 
   # -- CEPH CSI CephFS plugin resource requirement list
   # @default -- see values.yaml
@@ -340,6 +358,7 @@ csi:
           cpu: 50m
         limits:
           memory: 256Mi
+          cpu: 100m
     - name : csi-cephfsplugin
       resource:
         requests:
@@ -347,6 +366,7 @@ csi:
           cpu: 250m
         limits:
           memory: 1Gi
+          cpu: 500m
     - name : liveness-prometheus
       resource:
         requests:
@@ -354,6 +374,7 @@ csi:
           cpu: 50m
         limits:
           memory: 256Mi
+          cpu: 100m
 
   # -- CEPH CSI NFS provisioner resource requirement list
   # @default -- see values.yaml
@@ -365,6 +386,7 @@ csi:
           cpu: 100m
         limits:
           memory: 256Mi
+          cpu: 200m
     - name : csi-nfsplugin
       resource:
         requests:
@@ -372,6 +394,7 @@ csi:
           cpu: 250m
         limits:
           memory: 1Gi
+          cpu: 500m
     - name : csi-attacher
       resource:
         requests:
@@ -379,6 +402,7 @@ csi:
           cpu: 250m
         limits:
           memory: 1Gi
+          cpu: 500m
 
   # -- CEPH CSI NFS plugin resource requirement list
   # @default -- see values.yaml
@@ -390,6 +414,7 @@ csi:
           cpu: 50m
         limits:
           memory: 256Mi
+          cpu: 100m
     - name : csi-nfsplugin
       resource:
         requests:
@@ -397,6 +422,7 @@ csi:
           cpu: 250m
         limits:
           memory: 1Gi
+          cpu: 500m
 
   # Set provisionerTolerations and provisionerNodeAffinity for provisioner pod.
   # The CSI provisioner would be best to start on the same nodes as other ceph daemons.
@@ -469,7 +495,7 @@ csi:
 
   cephcsi:
     # -- Ceph CSI image
-    # @default -- `quay.io/cephcsi/cephcsi:v3.10.2`
+    # @default -- `quay.io/cephcsi/cephcsi:v3.10.1`
     image:
 
   registrar:
@@ -531,6 +557,17 @@ csi:
     # - topology.kubernetes.io/zone
     # - topology.rook.io/rack
 
+  readAffinity:
+    # -- Enable read affinity for RBD volumes. Recommended to
+    # set to true if running kernel 5.8 or newer.
+    # @default -- `false`
+    enabled: false
+    # -- Define which node labels to use
+    # as CRUSH location. This should correspond to the values set
+    # in the CRUSH map.
+    # @default -- labels listed [here](../CRDs/Cluster/ceph-cluster-crd.md#osd-topology)
+    crushLocationLabels:
+
   # -- Whether to skip any attach operation altogether for CephFS PVCs. See more details
   # [here](https://kubernetes-csi.github.io/docs/skip-attach.html#skip-attach-with-csi-driver-object).
   # If cephFSAttachRequired is set to false it skips the volume attachments and makes the creation
@@ -592,6 +629,7 @@ discover:
   # -- Add resources to discover daemon pods
   resources:
   #   - limits:
+  #       cpu: 500m
   #       memory: 512Mi
   #   - requests:
   #       cpu: 100m
diff --git a/deploy/examples/cluster-on-local-pvc.yaml b/deploy/examples/cluster-on-local-pvc.yaml
index c9118dbe7..96f6ac6c0 100644
--- a/deploy/examples/cluster-on-local-pvc.yaml
+++ b/deploy/examples/cluster-on-local-pvc.yaml
@@ -226,6 +226,7 @@ spec:
         resources:
         # These are the OSD daemon limits. For OSD prepare limits, see the separate section below for "prepareosd" resources
         #   limits:
+        #     cpu: "500m"
         #     memory: "4Gi"
         #   requests:
         #     cpu: "500m"
@@ -249,7 +250,10 @@ spec:
     onlyApplyOSDPlacement: false
   resources:
   #  prepareosd:
-  #    requests:
+  #    limits:
+  #      cpu: "200m"
+  #      memory: "200Mi"
+  #   requests:
   #      cpu: "200m"
   #      memory: "200Mi"
   priorityClassNames:
diff --git a/deploy/examples/cluster-on-pvc.yaml b/deploy/examples/cluster-on-pvc.yaml
index c6fea889e..a55773d6d 100644
--- a/deploy/examples/cluster-on-pvc.yaml
+++ b/deploy/examples/cluster-on-pvc.yaml
@@ -115,6 +115,7 @@ spec:
         resources:
         # These are the OSD daemon limits. For OSD prepare limits, see the separate section below for "prepareosd" resources
         #   limits:
+        #     cpu: "500m"
         #     memory: "4Gi"
         #   requests:
         #     cpu: "500m"
@@ -166,7 +167,10 @@ spec:
     onlyApplyOSDPlacement: false
   resources:
   #  prepareosd:
-  #    requests:
+  #    limits:
+  #      cpu: "200m"
+  #      memory: "200Mi"
+  #   requests:
   #      cpu: "200m"
   #      memory: "200Mi"
   priorityClassNames:
diff --git a/deploy/examples/cluster.yaml b/deploy/examples/cluster.yaml
index 57fc25790..8671af942 100644
--- a/deploy/examples/cluster.yaml
+++ b/deploy/examples/cluster.yaml
@@ -214,6 +214,7 @@ spec:
   #The requests and limits set here, allow the mgr pod to use half of one CPU core and 1 gigabyte of memory
   #   mgr:
   #     limits:
+  #       cpu: "500m"
   #       memory: "1024Mi"
   #     requests:
   #       cpu: "500m"
diff --git a/deploy/examples/csi/cephfs/kube-registry.yaml b/deploy/examples/csi/cephfs/kube-registry.yaml
index 681141ed2..22ca0df82 100644
--- a/deploy/examples/csi/cephfs/kube-registry.yaml
+++ b/deploy/examples/csi/cephfs/kube-registry.yaml
@@ -36,6 +36,7 @@ spec:
           imagePullPolicy: Always
           resources:
             limits:
+              cpu: 100m
               memory: 100Mi
           env:
             # Configuration reference: https://docs.docker.com/registry/configuration/
diff --git a/deploy/examples/filesystem-ec.yaml b/deploy/examples/filesystem-ec.yaml
index c65c1f793..baab5dc7b 100644
--- a/deploy/examples/filesystem-ec.yaml
+++ b/deploy/examples/filesystem-ec.yaml
@@ -85,6 +85,7 @@ spec:
     resources:
     # The requests and limits set here, allow the filesystem MDS Pod(s) to use half of one CPU core and 1 gigabyte of memory
     #  limits:
+    #    cpu: "500m"
     #    memory: "1024Mi"
     #  requests:
     #    cpu: "500m"
@@ -107,6 +108,6 @@ spec:
   # by default pinning is set with value: distributed=1
   # for disabling default values set (distributed=0)
   pinning:
-    distributed: 1 # distributed=<0, 1> (disabled=0)
+    distributed: 1            # distributed=<0, 1> (disabled=0)
     # export:                 # export=<0-256> (disabled=-1)
     # random:                 # random=[0.0, 1.0](disabled=0.0)
diff --git a/deploy/examples/filesystem-mirror.yaml b/deploy/examples/filesystem-mirror.yaml
index efcab7207..16b7eb259 100644
--- a/deploy/examples/filesystem-mirror.yaml
+++ b/deploy/examples/filesystem-mirror.yaml
@@ -28,8 +28,9 @@ spec:
   annotations:
   #  key: value
   resources:
-    # The requests and limits, for example to allow the cephfs-mirror pod(s) to use half of one CPU core and 1 gigabyte of memory
+  # The requests and limits, for example to allow the cephfs-mirror pod(s) to use half of one CPU core and 1 gigabyte of memory
     limits:
+      cpu: "500m"
       memory: "1Gi"
     requests:
       cpu: "500m"
diff --git a/deploy/examples/filesystem.yaml b/deploy/examples/filesystem.yaml
index 4cc72be01..385beb36d 100644
--- a/deploy/examples/filesystem.yaml
+++ b/deploy/examples/filesystem.yaml
@@ -103,6 +103,7 @@ spec:
     # resources:
     # The requests and limits set here, allow the filesystem MDS Pod(s) to use half of one CPU core and 1 gigabyte of memory
     #  limits:
+    #    cpu: "500m"
     #    memory: "1024Mi"
     #  requests:
     #    cpu: "500m"
@@ -114,27 +115,27 @@ spec:
       disabled: false
   # Filesystem mirroring settings
   # mirroring:
-  #   enabled: true
-  #   # list of Kubernetes Secrets containing the peer token
-  #   # for more details see: https://docs.ceph.com/en/latest/dev/cephfs-mirroring/#bootstrap-peers
-  #   # Add the secret name if it already exists else specify the empty list here.
-  #   peers:
-  #     secretNames:
-  #     - secondary-cluster-peer
-  #   # specify the schedule(s) on which snapshots should be taken
-  #   # see the official syntax here https://docs.ceph.com/en/latest/cephfs/snap-schedule/#add-and-remove-schedules
-  #   snapshotSchedules:
-  #     - path: /
-  #       interval: 24h # daily snapshots
-  #   # The startTime should be mentioned in the format YYYY-MM-DDTHH:MM:SS
-  #   # If startTime is not specified, then by default the start time is considered as midnight UTC.
-  #   # see usage here https://docs.ceph.com/en/latest/cephfs/snap-schedule/#usage
-  #   # startTime: 2022-07-15T11:55:00
-  #   # manage retention policies
-  #   # see syntax duration here https://docs.ceph.com/en/latest/cephfs/snap-schedule/#add-and-remove-retention-policies
-  #   snapshotRetention:
-  #     - path: /
-  #       duration: "h 24"
+    # enabled: true
+    # list of Kubernetes Secrets containing the peer token
+    # for more details see: https://docs.ceph.com/en/latest/dev/cephfs-mirroring/#bootstrap-peers
+    # Add the secret name if it already exists else specify the empty list here.
+    # peers:
+      #secretNames:
+        #- secondary-cluster-peer
+    # specify the schedule(s) on which snapshots should be taken
+    # see the official syntax here https://docs.ceph.com/en/latest/cephfs/snap-schedule/#add-and-remove-schedules
+    # snapshotSchedules:
+    #   - path: /
+    #     interval: 24h # daily snapshots
+        # The startTime should be mentioned in the format YYYY-MM-DDTHH:MM:SS
+        # If startTime is not specified, then by default the start time is considered as midnight UTC.
+        # see usage here https://docs.ceph.com/en/latest/cephfs/snap-schedule/#usage
+        # startTime: 2022-07-15T11:55:00
+    # manage retention policies
+    # see syntax duration here https://docs.ceph.com/en/latest/cephfs/snap-schedule/#add-and-remove-retention-policies
+    # snapshotRetention:
+    #   - path: /
+    #     duration: "h 24"
 ---
 # create default csi subvolume group
 apiVersion: ceph.rook.io/v1
@@ -152,6 +153,6 @@ spec:
   # by default pinning is set with value: distributed=1
   # for disabling default values set (distributed=0)
   pinning:
-    distributed: 1 # distributed=<0, 1> (disabled=0)
+    distributed: 1            # distributed=<0, 1> (disabled=0)
     # export:                 # export=<0-256> (disabled=-1)
     # random:                 # random=[0.0, 1.0](disabled=0.0)
diff --git a/deploy/examples/images.txt b/deploy/examples/images.txt
index 741f75b73..f7e14f7f7 100644
--- a/deploy/examples/images.txt
+++ b/deploy/examples/images.txt
@@ -1,7 +1,7 @@
  gcr.io/k8s-staging-sig-storage/objectstorage-sidecar/objectstorage-sidecar:v20230130-v0.1.0-24-gc0cf995
  quay.io/ceph/ceph:v18.2.1
  quay.io/ceph/cosi:v0.1.1
- quay.io/cephcsi/cephcsi:v3.10.2
+ quay.io/cephcsi/cephcsi:v3.10.1
  quay.io/csiaddons/k8s-sidecar:v0.8.0
  registry.k8s.io/sig-storage/csi-attacher:v4.4.2
  registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.9.1
diff --git a/deploy/examples/nfs.yaml b/deploy/examples/nfs.yaml
index ab57b59a7..b43ae3033 100644
--- a/deploy/examples/nfs.yaml
+++ b/deploy/examples/nfs.yaml
@@ -47,6 +47,7 @@ spec:
     # Resource requests and limits to apply to NFS server pods
     resources:
       # limits:
+      #   cpu: "3"
       #   memory: "8Gi"
       # requests:
       #   cpu: "3"
@@ -100,6 +101,7 @@ spec:
     #     # debugLevel: 6
     #     resources:
     #       limits:
+    #         cpu: "2"
     #         memory: "1Gi"
     #       requests:
     #         cpu: "2"
diff --git a/deploy/examples/object-ec.yaml b/deploy/examples/object-ec.yaml
index f3bcb80aa..fffa28ddb 100644
--- a/deploy/examples/object-ec.yaml
+++ b/deploy/examples/object-ec.yaml
@@ -74,6 +74,7 @@ spec:
     resources:
     # The requests and limits set here, allow the object store gateway Pod(s) to use half of one CPU core and 1 gigabyte of memory
     #  limits:
+    #    cpu: "500m"
     #    memory: "1024Mi"
     #  requests:
     #    cpu: "500m"
diff --git a/deploy/examples/object-openshift.yaml b/deploy/examples/object-openshift.yaml
index 228067390..e85eb176b 100644
--- a/deploy/examples/object-openshift.yaml
+++ b/deploy/examples/object-openshift.yaml
@@ -83,6 +83,7 @@ spec:
     resources:
     # The requests and limits set here, allow the object store gateway Pod(s) to use half of one CPU core and 1 gigabyte of memory
     #  limits:
+    #    cpu: "500m"
     #    memory: "1024Mi"
     #  requests:
     #    cpu: "500m"
diff --git a/deploy/examples/object.yaml b/deploy/examples/object.yaml
index 3755fb069..8ab1a801f 100644
--- a/deploy/examples/object.yaml
+++ b/deploy/examples/object.yaml
@@ -94,6 +94,7 @@ spec:
     resources:
     # The requests and limits set here, allow the object store gateway Pod(s) to use half of one CPU core and 1 gigabyte of memory
     #  limits:
+    #    cpu: "500m"
     #    memory: "1024Mi"
     #  requests:
     #    cpu: "500m"
diff --git a/deploy/examples/operator-openshift.yaml b/deploy/examples/operator-openshift.yaml
index 3d6f48049..35611ed2c 100644
--- a/deploy/examples/operator-openshift.yaml
+++ b/deploy/examples/operator-openshift.yaml
@@ -190,7 +190,7 @@ data:
   # The default version of CSI supported by Rook will be started. To change the version
   # of the CSI driver to something other than what is officially supported, change
   # these images to the desired release of the CSI driver.
-  # ROOK_CSI_CEPH_IMAGE: "quay.io/cephcsi/cephcsi:v3.10.2"
+  # ROOK_CSI_CEPH_IMAGE: "quay.io/cephcsi/cephcsi:v3.10.1"
   # ROOK_CSI_REGISTRAR_IMAGE: "registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.9.1"
   # ROOK_CSI_RESIZER_IMAGE: "registry.k8s.io/sig-storage/csi-resizer:v1.9.2"
   # ROOK_CSI_PROVISIONER_IMAGE: "registry.k8s.io/sig-storage/csi-provisioner:v3.6.3"
@@ -346,6 +346,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-resizer
   #    resource:
   #      requests:
@@ -353,6 +354,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-attacher
   #    resource:
   #      requests:
@@ -360,6 +362,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-snapshotter
   #    resource:
   #      requests:
@@ -367,6 +370,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-rbdplugin
   #    resource:
   #      requests:
@@ -374,6 +378,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
   #  - name : csi-omap-generator
   #    resource:
   #      requests:
@@ -381,6 +386,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
   #  - name : liveness-prometheus
   #    resource:
   #      requests:
@@ -388,6 +394,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
   # (Optional) CEPH CSI RBD plugin resource requirement list, Put here list of resource
   # requests and limits you want to apply for plugin pod
   #CSI_RBD_PLUGIN_RESOURCE: |
@@ -398,6 +405,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
   #  - name : csi-rbdplugin
   #    resource:
   #      requests:
@@ -405,6 +413,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
   #  - name : liveness-prometheus
   #    resource:
   #      requests:
@@ -412,6 +421,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
   # (Optional) CEPH CSI CephFS provisioner resource requirement list, Put here list of resource
   # requests and limits you want to apply for provisioner pod
   #CSI_CEPHFS_PROVISIONER_RESOURCE: |
@@ -422,6 +432,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-resizer
   #    resource:
   #      requests:
@@ -429,6 +440,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-attacher
   #    resource:
   #      requests:
@@ -436,6 +448,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-snapshotter
   #    resource:
   #      requests:
@@ -443,6 +456,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-cephfsplugin
   #    resource:
   #      requests:
@@ -450,6 +464,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
   #  - name : liveness-prometheus
   #    resource:
   #      requests:
@@ -457,6 +472,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
   # (Optional) CEPH CSI CephFS plugin resource requirement list, Put here list of resource
   # requests and limits you want to apply for plugin pod
   #CSI_CEPHFS_PLUGIN_RESOURCE: |
@@ -467,6 +483,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
   #  - name : csi-cephfsplugin
   #    resource:
   #      requests:
@@ -474,6 +491,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
   #  - name : liveness-prometheus
   #    resource:
   #      requests:
@@ -481,6 +499,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
 
   # (Optional) CEPH CSI NFS provisioner resource requirement list, Put here list of resource
   # requests and limits you want to apply for provisioner pod
@@ -492,6 +511,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-nfsplugin
   #    resource:
   #      requests:
@@ -499,6 +519,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
   #  - name : csi-attacher
   #    resource:
   #      requests:
@@ -506,6 +527,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   # (Optional) CEPH CSI NFS plugin resource requirement list, Put here list of resource
   # requests and limits you want to apply for plugin pod
   # CSI_NFS_PLUGIN_RESOURCE: |
@@ -516,6 +538,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
   #  - name : csi-nfsplugin
   #    resource:
   #      requests:
@@ -523,6 +546,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
 
   # Configure CSI CephFS liveness metrics port
   # Set to true to enable Ceph CSI liveness container.
@@ -560,6 +584,16 @@ data:
   # updated with node labels that define domains of interest
   # CSI_TOPOLOGY_DOMAIN_LABELS: "kubernetes.io/hostname,topology.kubernetes.io/zone,topology.rook.io/rack"
 
+  # Enable read affinity for RBD volumes. Recommended to
+  # set to true if running kernel 5.8 or newer.
+  CSI_ENABLE_READ_AFFINITY: "false"
+  # CRUSH location labels define which node labels to use
+  # as CRUSH location. This should correspond to the values set in
+  # the CRUSH map.
+  # Defaults to all the labels mentioned in
+  # https://rook.io/docs/rook/latest/CRDs/Cluster/ceph-cluster-crd/#osd-topology
+  # CSI_CRUSH_LOCATION_LABELS: "kubernetes.io/hostname,topology.kubernetes.io/zone,topology.rook.io/rack"
+
   # Whether to skip any attach operation altogether for CephCSI PVCs.
   # See more details [here](https://kubernetes-csi.github.io/docs/skip-attach.html#skip-attach-with-csi-driver-object).
   # If set to false it skips the volume attachments and makes the creation of pods using the CephCSI PVC fast.
@@ -614,6 +648,7 @@ data:
   #   - name: DISCOVER_DAEMON_RESOURCES
   #     resources:
   #       limits:
+  #         cpu: 1500m
   #         memory: 512Mi
   #       requests:
   #         cpu: 200m
@@ -689,6 +724,7 @@ spec:
             #   value: |
             #     resources:
             #       limits:
+            #         cpu: 500m
             #         memory: 512Mi
             #       requests:
             #         cpu: 100m
@@ -727,6 +763,7 @@ spec:
           # Recommended resource requests and limits, if desired
           #resources:
           #  limits:
+          #    cpu: 500m
           #    memory: 512Mi
           #  requests:
           #    cpu: 100m
diff --git a/deploy/examples/operator.yaml b/deploy/examples/operator.yaml
index b5f16fee8..a41ffe5ef 100644
--- a/deploy/examples/operator.yaml
+++ b/deploy/examples/operator.yaml
@@ -112,7 +112,7 @@ data:
   # The default version of CSI supported by Rook will be started. To change the version
   # of the CSI driver to something other than what is officially supported, change
   # these images to the desired release of the CSI driver.
-  # ROOK_CSI_CEPH_IMAGE: "quay.io/cephcsi/cephcsi:v3.10.2"
+  # ROOK_CSI_CEPH_IMAGE: "quay.io/cephcsi/cephcsi:v3.10.1"
   # ROOK_CSI_REGISTRAR_IMAGE: "registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.9.1"
   # ROOK_CSI_RESIZER_IMAGE: "registry.k8s.io/sig-storage/csi-resizer:v1.9.2"
   # ROOK_CSI_PROVISIONER_IMAGE: "registry.k8s.io/sig-storage/csi-provisioner:v3.6.3"
@@ -279,6 +279,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-resizer
   #    resource:
   #      requests:
@@ -286,6 +287,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-attacher
   #    resource:
   #      requests:
@@ -293,6 +295,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-snapshotter
   #    resource:
   #      requests:
@@ -300,6 +303,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-rbdplugin
   #    resource:
   #      requests:
@@ -307,6 +311,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
   #  - name : csi-omap-generator
   #    resource:
   #      requests:
@@ -314,6 +319,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
   #  - name : liveness-prometheus
   #    resource:
   #      requests:
@@ -321,6 +327,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
   # (Optional) CEPH CSI RBD plugin resource requirement list, Put here list of resource
   # requests and limits you want to apply for plugin pod
   #CSI_RBD_PLUGIN_RESOURCE: |
@@ -331,6 +338,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
   #  - name : csi-rbdplugin
   #    resource:
   #      requests:
@@ -338,6 +346,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
   #  - name : liveness-prometheus
   #    resource:
   #      requests:
@@ -345,6 +354,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
   # (Optional) CEPH CSI CephFS provisioner resource requirement list, Put here list of resource
   # requests and limits you want to apply for provisioner pod
   #CSI_CEPHFS_PROVISIONER_RESOURCE: |
@@ -355,6 +365,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-resizer
   #    resource:
   #      requests:
@@ -362,6 +373,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-attacher
   #    resource:
   #      requests:
@@ -369,6 +381,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-snapshotter
   #    resource:
   #      requests:
@@ -376,6 +389,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-cephfsplugin
   #    resource:
   #      requests:
@@ -383,6 +397,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
   #  - name : liveness-prometheus
   #    resource:
   #      requests:
@@ -390,6 +405,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
   # (Optional) CEPH CSI CephFS plugin resource requirement list, Put here list of resource
   # requests and limits you want to apply for plugin pod
   #CSI_CEPHFS_PLUGIN_RESOURCE: |
@@ -400,6 +416,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
   #  - name : csi-cephfsplugin
   #    resource:
   #      requests:
@@ -407,6 +424,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
   #  - name : liveness-prometheus
   #    resource:
   #      requests:
@@ -414,6 +432,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
 
   # (Optional) CEPH CSI NFS provisioner resource requirement list, Put here list of resource
   # requests and limits you want to apply for provisioner pod
@@ -425,6 +444,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   #  - name : csi-nfsplugin
   #    resource:
   #      requests:
@@ -432,6 +452,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
   #  - name : csi-attacher
   #    resource:
   #      requests:
@@ -439,6 +460,7 @@ data:
   #        cpu: 100m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 200m
   # (Optional) CEPH CSI NFS plugin resource requirement list, Put here list of resource
   # requests and limits you want to apply for plugin pod
   # CSI_NFS_PLUGIN_RESOURCE: |
@@ -449,6 +471,7 @@ data:
   #        cpu: 50m
   #      limits:
   #        memory: 256Mi
+  #        cpu: 100m
   #  - name : csi-nfsplugin
   #    resource:
   #      requests:
@@ -456,6 +479,7 @@ data:
   #        cpu: 250m
   #      limits:
   #        memory: 1Gi
+  #        cpu: 500m
 
   # Configure CSI CephFS liveness metrics port
   # Set to true to enable Ceph CSI liveness container.
@@ -502,6 +526,16 @@ data:
   # updated with node labels that define domains of interest
   # CSI_TOPOLOGY_DOMAIN_LABELS: "kubernetes.io/hostname,topology.kubernetes.io/zone,topology.rook.io/rack"
 
+  # Enable read affinity for RBD volumes. Recommended to
+  # set to true if running kernel 5.8 or newer.
+  CSI_ENABLE_READ_AFFINITY: "false"
+  # CRUSH location labels define which node labels to use
+  # as CRUSH location. This should correspond to the values set in
+  # the CRUSH map.
+  # Defaults to all the labels mentioned in
+  # https://rook.io/docs/rook/latest/CRDs/Cluster/ceph-cluster-crd/#osd-topology
+  # CSI_CRUSH_LOCATION_LABELS: "kubernetes.io/hostname,topology.kubernetes.io/zone,topology.rook.io/rack"
+
   # Whether to skip any attach operation altogether for CephCSI PVCs.
   # See more details [here](https://kubernetes-csi.github.io/docs/skip-attach.html#skip-attach-with-csi-driver-object).
   # If set to false it skips the volume attachments and makes the creation of pods using the CephCSI PVC fast.
@@ -540,6 +574,7 @@ data:
   #   - name: DISCOVER_DAEMON_RESOURCES
   #     resources:
   #       limits:
+  #         cpu: 500m
   #         memory: 512Mi
   #       requests:
   #         cpu: 100m
@@ -642,6 +677,7 @@ spec:
           # Recommended resource requests and limits, if desired
           #resources:
           #  limits:
+          #    cpu: 1500m
           #    memory: 512Mi
           #  requests:
           #    cpu: 200m
diff --git a/deploy/examples/rbdmirror.yaml b/deploy/examples/rbdmirror.yaml
index 0957561fe..a50997408 100644
--- a/deploy/examples/rbdmirror.yaml
+++ b/deploy/examples/rbdmirror.yaml
@@ -14,8 +14,8 @@ spec:
   # list of Kubernetes Secrets containing the peer token
   # for more details see: https://docs.ceph.com/docs/master/rbd/rbd-mirroring/#bootstrap-peers
   #peers:
-  #  secretNames:
-  #  - secondary-cluster-peer
+    #secretNames:
+      #- secondary-cluster-peer
   # The affinity rules to apply to the rbd-mirror deployment
   placement:
   #  nodeAffinity:
@@ -35,8 +35,9 @@ spec:
   annotations:
   #  key: value
   resources:
-    # The requests and limits, for example to allow the rbd-mirror pod(s) to use half of one CPU core and 1 gigabyte of memory
+  # The requests and limits, for example to allow the rbd-mirror pod(s) to use half of one CPU core and 1 gigabyte of memory
     limits:
+      cpu: "500m"
       memory: "1Gi"
     requests:
       cpu: "500m"
diff --git a/design/ceph/ceph-nfs-ganesha.md b/design/ceph/ceph-nfs-ganesha.md
index a26647f62..68b121176 100644
--- a/design/ceph/ceph-nfs-ganesha.md
+++ b/design/ceph/ceph-nfs-ganesha.md
@@ -112,6 +112,7 @@ spec:
     # one CPU core and 1 gigabyte of memory
     resources:
     #  limits:
+    #    cpu: "3"
     #    memory: "8Gi"
     #  requests:
     #    cpu: "3"
@@ -141,6 +142,7 @@ spec:
           #   cpu: "2"
           #   memory: "1024Mi"
           # limits:
+          #   cpu: "2"
           #   memory: "1024Mi"
 
     kerberos:
diff --git a/design/ceph/object/ceph-cosi-driver.md b/design/ceph/object/ceph-cosi-driver.md
index aaae1be08..bcf467edd 100644
--- a/design/ceph/object/ceph-cosi-driver.md
+++ b/design/ceph/object/ceph-cosi-driver.md
@@ -66,6 +66,7 @@ spec:
   #    cpu: "100m"
   #    memory: "128Mi"
   #  limits:
+  #    cpu: "100m"
   #    memory: "128Mi"
 ```
 
diff --git a/design/ceph/resource-constraints.md b/design/ceph/resource-constraints.md
index 7d103a479..8363d7bc5 100644
--- a/design/ceph/resource-constraints.md
+++ b/design/ceph/resource-constraints.md
@@ -92,24 +92,28 @@ spec:
           cpu: "500m"
           memory: "512Mi"
       limits:
+        cpu: "500m"
         memory: "512Mi"
     mgr:
       requests:
           cpu: "500m"
           memory: "512Mi"
       limits:
+        cpu: "500m"
         memory: "512Mi"
     mon:
       requests:
           cpu: "500m"
           memory: "512Mi"
       limits:
+        cpu: "500m"
         memory: "512Mi"
     osd:
       requests:
           cpu: "500m"
           memory: "512Mi"
       limits:
+        cpu: "500m"
         memory: "512Mi"
   storage:
     useAllNodes: true
@@ -138,4 +142,6 @@ spec:
         requests:
           memory: "512Mi"
           cpu: "1"
+        limits:
+          cpu: "2"
 ```
diff --git a/design/common/multiple-storage-types-support.md b/design/common/multiple-storage-types-support.md
index 00ace0890..e2ef1b911 100644
--- a/design/common/multiple-storage-types-support.md
+++ b/design/common/multiple-storage-types-support.md
@@ -377,6 +377,7 @@ spec:
   resources:
   - name: osd
     limits:
+      cpu: "500m"
       memory: "1024Mi"
     requests:
       cpu: "500m"
diff --git a/go.mod b/go.mod
index ebd7b31a5..b5f9b0ff1 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ replace github.com/rook/rook/pkg/apis => ./pkg/apis
 
 require (
 	github.com/IBM/keyprotect-go-client v0.12.2
-	github.com/aws/aws-sdk-go v1.50.12
+	github.com/aws/aws-sdk-go v1.50.5
 	github.com/banzaicloud/k8s-objectmatcher v1.8.0
 	github.com/ceph/go-ceph v0.25.0
 	github.com/coreos/pkg v0.0.0-20230601102743-20bbbf26f4d8
diff --git a/go.sum b/go.sum
index 61150cc2a..f3b575783 100644
--- a/go.sum
+++ b/go.sum
@@ -111,8 +111,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY
 github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 github.com/aws/aws-sdk-go v1.44.164/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
-github.com/aws/aws-sdk-go v1.50.12 h1:Gc6QS4Ys++cWSl63U+HyPbKeLVcoOvi6veayhcipPac=
-github.com/aws/aws-sdk-go v1.50.12/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
+github.com/aws/aws-sdk-go v1.50.5 h1:H2Aadcgwr7a2aqS6ZwcE+l1mA6ZrTseYCvjw2QLmxIA=
+github.com/aws/aws-sdk-go v1.50.5/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
 github.com/banzaicloud/k8s-objectmatcher v1.8.0 h1:Nugn25elKtPMTA2br+JgHNeSQ04sc05MDPmpJnd1N2A=
 github.com/banzaicloud/k8s-objectmatcher v1.8.0/go.mod h1:p2LSNAjlECf07fbhDyebTkPUIYnU05G+WfGgkTmgeMg=
 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
diff --git a/pkg/daemon/ceph/osd/volume.go b/pkg/daemon/ceph/osd/volume.go
index 03b5259db..b127f265d 100644
--- a/pkg/daemon/ceph/osd/volume.go
+++ b/pkg/daemon/ceph/osd/volume.go
@@ -47,10 +47,6 @@ const (
 	dbDeviceFlag         = "--db-devices"
 	cephVolumeCmd        = "ceph-volume"
 	cephVolumeMinDBSize  = 1024 // 1GB
-
-	blockDBFlag     = "--block.db"
-	blockDBSizeFlag = "--block.db-size"
-	dataFlag        = "--data"
 )
 
 // These are not constants because they are used by the tests
@@ -669,12 +665,6 @@ func (a *OsdAgent) initializeDevicesLVMMode(context *clusterd.Context, devices *
 					}
 					metadataDevices[md]["devices"] = deviceArg
 				}
-				if metadataDevice.Type == sys.PartType {
-					if a.metadataDevice != "" && device.Config.MetadataDevice == "" {
-						return errors.Errorf("Partition device %s can not be specified as metadataDevice in the global OSD configuration or in the node level OSD configuration", md)
-					}
-					metadataDevices[md]["part"] = "true" // ceph-volume lvm batch only supports disk and lvm
-				}
 				deviceDBSizeMB := getDatabaseSize(a.storeConfig.DatabaseSizeMB, device.Config.DatabaseSizeMB)
 				if a.storeConfig.IsValidStoreType() && deviceDBSizeMB > 0 {
 					if deviceDBSizeMB < cephVolumeMinDBSize {
@@ -731,110 +721,71 @@ func (a *OsdAgent) initializeDevicesLVMMode(context *clusterd.Context, devices *
 
 	for md, conf := range metadataDevices {
 
-		// Do not change device names if udev persistent names are passed
-		mdPath := md
-		if !strings.HasPrefix(mdPath, "/dev") {
-			mdPath = path.Join("/dev", md)
-		}
-
-		var hasPart bool
 		mdArgs := batchArgs
-		osdsPerDevice := 1
-		if part, ok := conf["part"]; ok && part == "true" {
-			hasPart = true
-		}
-		if hasPart {
-			// ceph-volume lvm prepare --data {vg/lv} --block.wal {partition} --block.db {/path/to/device}
-			baseArgs := []string{"-oL", cephVolumeCmd, "--log-path", logPath, "lvm", "prepare", storeFlag}
-			if a.storeConfig.EncryptedDevice {
-				baseArgs = append(baseArgs, encryptedFlag)
-			}
-			mdArgs = baseArgs
-			devices := strings.Split(conf["devices"], " ")
-			if len(devices) > 1 {
-				logger.Warningf("partition metadataDevice %s can only be used by one data device", md)
-			}
-			if _, ok := conf["osdsperdevice"]; ok {
-				logger.Warningf("`ceph-volume osd prepare` doesn't support multiple OSDs per device")
-			}
+		if _, ok := conf["osdsperdevice"]; ok {
 			mdArgs = append(mdArgs, []string{
-				dataFlag,
-				devices[0],
-				blockDBFlag,
-				mdPath,
-			}...)
-			if _, ok := conf["databasesizemb"]; ok {
-				mdArgs = append(mdArgs, []string{
-					blockDBSizeFlag,
-					conf["databasesizemb"],
-				}...)
-			}
-		} else {
-			if _, ok := conf["osdsperdevice"]; ok {
-				mdArgs = append(mdArgs, []string{
-					osdsPerDeviceFlag,
-					conf["osdsperdevice"],
-				}...)
-				v, _ := strconv.Atoi(conf["osdsperdevice"])
-				if v > 1 {
-					osdsPerDevice = v
-				}
-			}
-			if _, ok := conf["databasesizemb"]; ok {
-				mdArgs = append(mdArgs, []string{
-					databaseSizeFlag,
-					conf["databasesizemb"],
-				}...)
-			}
-			mdArgs = append(mdArgs, strings.Split(conf["devices"], " ")...)
-			mdArgs = append(mdArgs, []string{
-				dbDeviceFlag,
-				mdPath,
+				osdsPerDeviceFlag,
+				conf["osdsperdevice"],
 			}...)
 		}
-
 		if _, ok := conf["deviceclass"]; ok {
 			mdArgs = append(mdArgs, []string{
 				crushDeviceClassFlag,
 				conf["deviceclass"],
 			}...)
 		}
-
-		if !hasPart {
-			// Reporting
-			reportArgs := append(mdArgs, []string{
-				"--report",
+		if _, ok := conf["databasesizemb"]; ok {
+			mdArgs = append(mdArgs, []string{
+				databaseSizeFlag,
+				conf["databasesizemb"],
 			}...)
+		}
+		mdArgs = append(mdArgs, strings.Split(conf["devices"], " ")...)
 
-			if err := context.Executor.ExecuteCommand(baseCommand, reportArgs...); err != nil {
-				return errors.Wrap(err, "failed ceph-volume report") // fail return here as validation provided by ceph-volume
-			}
+		// Do not change device names if udev persistent names are passed
+		mdPath := md
+		if !strings.HasPrefix(mdPath, "/dev") {
+			mdPath = path.Join("/dev", md)
+		}
 
-			reportArgs = append(reportArgs, []string{
-				"--format",
-				"json",
-			}...)
+		mdArgs = append(mdArgs, []string{
+			dbDeviceFlag,
+			mdPath,
+		}...)
 
-			cvOut, err := context.Executor.ExecuteCommandWithOutput(baseCommand, reportArgs...)
-			if err != nil {
-				return errors.Wrapf(err, "failed ceph-volume json report: %s", cvOut) // fail return here as validation provided by ceph-volume
-			}
+		// Reporting
+		reportArgs := append(mdArgs, []string{
+			"--report",
+		}...)
 
-			logger.Debugf("ceph-volume reports: %+v", cvOut)
+		if err := context.Executor.ExecuteCommand(baseCommand, reportArgs...); err != nil {
+			return errors.Wrap(err, "failed ceph-volume report") // fail return here as validation provided by ceph-volume
+		}
 
-			var cvReports []cephVolReportV2
-			if err = json.Unmarshal([]byte(cvOut), &cvReports); err != nil {
-				return errors.Wrap(err, "failed to unmarshal ceph-volume report json")
-			}
+		reportArgs = append(reportArgs, []string{
+			"--format",
+			"json",
+		}...)
 
-			if len(strings.Split(conf["devices"], " "))*osdsPerDevice != len(cvReports) {
-				return errors.Errorf("failed to create enough required devices, required: %s, actual: %v", cvOut, cvReports)
-			}
+		cvOut, err := context.Executor.ExecuteCommandWithOutput(baseCommand, reportArgs...)
+		if err != nil {
+			return errors.Wrapf(err, "failed ceph-volume json report: %s", cvOut) // fail return here as validation provided by ceph-volume
+		}
 
-			for _, report := range cvReports {
-				if report.BlockDB != mdPath && !strings.HasSuffix(mdPath, report.BlockDB) {
-					return errors.Errorf("wrong db device for %s, required: %s, actual: %s", report.Data, mdPath, report.BlockDB)
-				}
+		logger.Debugf("ceph-volume reports: %+v", cvOut)
+
+		var cvReports []cephVolReportV2
+		if err = json.Unmarshal([]byte(cvOut), &cvReports); err != nil {
+			return errors.Wrap(err, "failed to unmarshal ceph-volume report json")
+		}
+
+		if len(strings.Split(conf["devices"], " ")) != len(cvReports) {
+			return errors.Errorf("failed to create enough required devices, required: %s, actual: %v", cvOut, cvReports)
+		}
+
+		for _, report := range cvReports {
+			if report.BlockDB != mdPath && !strings.HasSuffix(mdPath, report.BlockDB) {
+				return errors.Errorf("wrong db device for %s, required: %s, actual: %s", report.Data, mdPath, report.BlockDB)
 			}
 		}
 
diff --git a/pkg/operator/ceph/cluster/watcher.go b/pkg/operator/ceph/cluster/watcher.go
index ab1e3f7b8..1fbeb1bfe 100644
--- a/pkg/operator/ceph/cluster/watcher.go
+++ b/pkg/operator/ceph/cluster/watcher.go
@@ -533,6 +533,9 @@ func (c *clientCluster) createNetworkFence(ctx context.Context, pv corev1.Persis
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      fenceResourceName(node.Name, driver),
 			Namespace: cluster.Namespace,
+			OwnerReferences: []metav1.OwnerReference{
+				*metav1.NewControllerRef(cluster, cephv1.SchemeGroupVersion.WithKind("CephCluster")),
+			},
 		},
 		Spec: addonsv1alpha1.NetworkFenceSpec{
 			Driver:     pv.Spec.CSI.Driver,
diff --git a/pkg/operator/ceph/csi/csi.go b/pkg/operator/ceph/csi/csi.go
index 48bc7d8eb..5415ed626 100644
--- a/pkg/operator/ceph/csi/csi.go
+++ b/pkg/operator/ceph/csi/csi.go
@@ -21,6 +21,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/rook/rook/pkg/operator/ceph/cluster/osd/topology"
 	"github.com/rook/rook/pkg/operator/k8sutil"
 
 	"github.com/pkg/errors"
@@ -83,6 +84,11 @@ func (r *ReconcileCSI) setParams(ver *version.Info) error {
 		return errors.Wrap(err, "failed to parse value for 'CSI_ENABLE_HOST_NETWORK'")
 	}
 
+	if CSIParam.EnableReadAffinity, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_READ_AFFINITY", "false")); err != nil {
+		return errors.Wrap(err, "failed to parse value for 'CSI_ENABLE_READ_AFFINITY'")
+	}
+	CSIParam.CrushLocationLabels = k8sutil.GetValue(r.opConfig.Parameters, "CSI_CRUSH_LOCATION_LABELS", topology.GetDefaultTopologyLabels())
+
 	// If not set or set to anything but "false", the kernel client will be enabled
 	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_FORCE_CEPHFS_KERNEL_CLIENT", "true"), "false") {
 		CSIParam.ForceCephFSKernelClient = "false"
diff --git a/pkg/operator/ceph/csi/spec.go b/pkg/operator/ceph/csi/spec.go
index 4e08c1aaf..f4ef79865 100644
--- a/pkg/operator/ceph/csi/spec.go
+++ b/pkg/operator/ceph/csi/spec.go
@@ -65,6 +65,7 @@ type Param struct {
 	ImagePullPolicy                          string
 	CSIClusterName                           string
 	CSIDomainLabels                          string
+	CrushLocationLabels                      string
 	GRPCTimeout                              time.Duration
 	CSIEnableMetadata                        bool
 	EnablePluginSelinuxHostMount             bool
@@ -79,6 +80,7 @@ type Param struct {
 	EnableCSIEncryption                      bool
 	EnableCSITopology                        bool
 	EnableLiveness                           bool
+	EnableReadAffinity                       bool
 	CephFSAttachRequired                     bool
 	RBDAttachRequired                        bool
 	NFSAttachRequired                        bool
@@ -136,7 +138,7 @@ var (
 // manually challenging.
 var (
 	// image names
-	DefaultCSIPluginImage   = "quay.io/cephcsi/cephcsi:v3.10.2"
+	DefaultCSIPluginImage   = "quay.io/cephcsi/cephcsi:v3.10.1"
 	DefaultRegistrarImage   = "registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.9.1"
 	DefaultProvisionerImage = "registry.k8s.io/sig-storage/csi-provisioner:v3.6.3"
 	DefaultAttacherImage    = "registry.k8s.io/sig-storage/csi-attacher:v4.4.2"
diff --git a/pkg/operator/ceph/csi/template/rbd/csi-rbdplugin.yaml b/pkg/operator/ceph/csi/template/rbd/csi-rbdplugin.yaml
index 960598ace..95153f402 100644
--- a/pkg/operator/ceph/csi/template/rbd/csi-rbdplugin.yaml
+++ b/pkg/operator/ceph/csi/template/rbd/csi-rbdplugin.yaml
@@ -79,6 +79,10 @@ spec:
             {{- if .EnableCSITopology }}
             - "--domainlabels={{ .CSIDomainLabels }}"
             {{- end }}
+            {{- if .EnableReadAffinity }}
+            - "--enable-read-affinity=true"
+            - "--crush-location-labels={{ .CrushLocationLabels }}"
+            {{- end }}
           env:
             - name: POD_IP
               valueFrom:
diff --git a/pkg/operator/ceph/disruption/clusterdisruption/osd.go b/pkg/operator/ceph/disruption/clusterdisruption/osd.go
index 15bfbd137..00e82752b 100644
--- a/pkg/operator/ceph/disruption/clusterdisruption/osd.go
+++ b/pkg/operator/ceph/disruption/clusterdisruption/osd.go
@@ -338,9 +338,24 @@ func (r *ReconcileClusterDisruption) reconcilePDBsForOSDs(
 	}
 
 	switch {
-	// osd is down
-	case osdDown:
-		logger.Infof("osd is down in failure domain %q. pg health: %q", drainingFailureDomain, pgHealthMsg)
+	// osd is down but pgs are active+clean
+	case osdDown && pgClean:
+		lastDrainTimeStamp, err := getLastDrainTimeStamp(pdbStateMap, drainingFailureDomainDurationKey)
+		if err != nil {
+			return reconcile.Result{}, errors.Wrapf(err, "failed to get last drain timestamp from the configmap %q", pdbStateMap.Name)
+		}
+		timeSinceOSDDown := time.Since(lastDrainTimeStamp)
+		if timeSinceOSDDown > 30*time.Second {
+			logger.Infof("osd is down in failure domain %q is down for the last %.2f minutes, but pgs are active+clean", drainingFailureDomain, timeSinceOSDDown.Minutes())
+			resetPDBConfig(pdbStateMap)
+		} else {
+			logger.Infof("osd is down in the failure domain %q, but pgs are active+clean. Requeuing in case pg status is not updated yet...", drainingFailureDomain)
+			return reconcile.Result{Requeue: true, RequeueAfter: 15 * time.Second}, nil
+		}
+
+	// osd is down and pgs are not healthy
+	case osdDown && !pgClean:
+		logger.Infof("osd is down in failure domain %q and pgs are not active+clean. pg health: %q", drainingFailureDomain, pgHealthMsg)
 		currentlyDrainingFD, ok := pdbStateMap.Data[drainingFailureDomainKey]
 		if !ok || drainingFailureDomain != currentlyDrainingFD {
 			pdbStateMap.Data[drainingFailureDomainKey] = drainingFailureDomain
@@ -368,7 +383,7 @@ func (r *ReconcileClusterDisruption) reconcilePDBsForOSDs(
 		}
 	}
 
-	if pdbStateMap.Data[drainingFailureDomainKey] != "" {
+	if pdbStateMap.Data[drainingFailureDomainKey] != "" && !pgClean {
 		// delete default OSD pdb and create blocking OSD pdbs
 		err := r.handleActiveDrains(allFailureDomains, pdbStateMap.Data[drainingFailureDomainKey], failureDomainType, clusterInfo.Namespace, pgClean)
 		if err != nil {
@@ -631,6 +646,22 @@ func getPDBName(failureDomainType, failureDomainName string) string {
 	return k8sutil.TruncateNodeName(fmt.Sprintf("%s-%s-%s", osdPDBAppName, failureDomainType, "%s"), failureDomainName)
 }
 
+func getLastDrainTimeStamp(pdbStateMap *corev1.ConfigMap, key string) (time.Time, error) {
+	var err error
+	var lastDrainTimeStamp time.Time
+	lastDrainTimeStampString, ok := pdbStateMap.Data[key]
+	if !ok || len(lastDrainTimeStampString) == 0 {
+		return time.Now(), nil
+	} else {
+		lastDrainTimeStamp, err = time.Parse(time.RFC3339, pdbStateMap.Data[key])
+		if err != nil {
+			return time.Time{}, errors.Wrapf(err, "failed to parse timestamp %q", pdbStateMap.Data[key])
+		}
+	}
+
+	return lastDrainTimeStamp, nil
+}
+
 func (r *ReconcileClusterDisruption) getAllowedDisruptions(pdbName, namespace string) (int32, error) {
 	usePDBV1Beta1, err := k8sutil.UsePDBV1Beta1Version(r.context.ClusterdContext.Clientset)
 	if err != nil {


Test patch:
diff --git a/.github/workflows/canary-integration-test.yml b/.github/workflows/canary-integration-test.yml
index af0b3964c..19d088a66 100644
--- a/.github/workflows/canary-integration-test.yml
+++ b/.github/workflows/canary-integration-test.yml
@@ -13,11 +13,12 @@ on:
     paths-ignore:
       - "Documentation/**"
       - "design/**"
-  workflow_dispatch:
+  workflow_call:
     inputs:
       ceph-image:
         description: 'Ceph image for creating Ceph cluster'
         default: 'quay.io/ceph/ceph:v18'
+        type: string
 
 defaults:
   run:
@@ -391,51 +392,6 @@ jobs:
         with:
           name: canary
 
-  osd-with-metadata-partition-device:
-    runs-on: ubuntu-20.04
-    if: "!contains(github.event.pull_request.labels.*.name, 'skip-ci')"
-    steps:
-      - name: checkout
-        uses: actions/checkout@v4
-        with:
-          fetch-depth: 0
-
-      - name: consider debugging
-        uses: ./.github/workflows/tmate_debug
-        with:
-          use-tmate: ${{ secrets.USE_TMATE }}
-
-      - name: setup cluster resources
-        uses: ./.github/workflows/canary-test-config
-
-      - name: validate-yaml
-        run: tests/scripts/github-action-helper.sh validate_yaml
-
-      - name: use local disk as OSD metadata partition
-        run: |
-          export BLOCK="/dev/$(tests/scripts/github-action-helper.sh find_extra_block_dev)"
-          tests/scripts/github-action-helper.sh use_local_disk
-          tests/scripts/create-bluestore-partitions.sh --disk "$BLOCK" --bluestore-type block.db --osd-count 1
-
-      - name: deploy cluster
-        run: |
-          tests/scripts/github-action-helper.sh deploy_cluster osd_with_metadata_partition_device
-
-      - name: wait for prepare pod
-        run: tests/scripts/github-action-helper.sh wait_for_prepare_pod 1
-
-      - name: wait for ceph to be ready
-        run: tests/scripts/github-action-helper.sh wait_for_ceph_to_be_ready osd 1
-
-      - name: check-ownerreferences
-        run: tests/scripts/github-action-helper.sh check_ownerreferences
-
-      - name: collect common logs
-        if: always()
-        uses: ./.github/workflows/collect-logs
-        with:
-          name: canary
-
   osd-with-metadata-device:
     runs-on: ubuntu-20.04
     if: "!contains(github.event.pull_request.labels.*.name, 'skip-ci')"
@@ -1404,7 +1360,6 @@ jobs:
           fetch-depth: 0
 
       - name: consider debugging
-
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
@@ -1431,7 +1386,6 @@ jobs:
           fetch-depth: 0
 
       - name: consider debugging
-
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
@@ -1464,7 +1418,6 @@ jobs:
           fetch-depth: 0
 
       - name: consider debugging
-
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
@@ -1546,7 +1499,6 @@ jobs:
           fetch-depth: 0
 
       - name: consider debugging
-
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
diff --git a/pkg/daemon/ceph/osd/volume_test.go b/pkg/daemon/ceph/osd/volume_test.go
index 6c6cb64e4..95366f040 100644
--- a/pkg/daemon/ceph/osd/volume_test.go
+++ b/pkg/daemon/ceph/osd/volume_test.go
@@ -626,7 +626,6 @@ func TestConfigureCVDevices(t *testing.T) {
 }
 
 func testBaseArgs(args []string) error {
-	// stdbuf -oL ceph-volume --log-path /tmp/ceph-log lvm batch --prepare --bluestore --yes --osds-per-device 1 --crush-device-class hdd /dev/sda --db-devices /dev/sdl1 --report
 	if args[1] == "ceph-volume" && args[2] == "--log-path" && args[3] == "/tmp/ceph-log" && args[4] == "lvm" && args[5] == "batch" && args[6] == "--prepare" && args[7] == "--bluestore" && args[8] == "--yes" {
 		return nil
 	}
@@ -634,15 +633,6 @@ func testBaseArgs(args []string) error {
 	return errors.Errorf("unknown args %s ", args)
 }
 
-func testBasePrepareArgs(args []string) error {
-	// stdbuf -oL ceph-volume --log-path /tmp/ceph-log lvm prepare --bluestore --crush-device-class hdd --data /dev/sda --block.db /dev/sdj1
-	if args[1] == "ceph-volume" && args[2] == "--log-path" && args[3] == "/tmp/ceph-log" && args[4] == "lvm" && args[5] == "prepare" && args[6] == "--bluestore" {
-		return nil
-	}
-
-	return errors.Errorf("unknown args %s ", args)
-}
-
 func TestInitializeBlock(t *testing.T) {
 	os.Setenv(oposd.OSDStoreTypeVarName, "bluestore")
 	// Common vars for all the tests
@@ -779,7 +769,7 @@ func TestInitializeBlock(t *testing.T) {
 		logger.Info("success, go to next test")
 	}
 
-	// Test with metadata device and one osd per device
+	// Test with metadata devices
 	{
 		devices := &DeviceOsdMapping{
 			Entries: map[string]*DeviceOsdIDEntry{
@@ -842,270 +832,6 @@ func TestInitializeBlock(t *testing.T) {
 		}
 	}
 
-	// Test with metadata device and multiple osds per device
-	{
-		devices := &DeviceOsdMapping{
-			Entries: map[string]*DeviceOsdIDEntry{
-				"sda": {Data: -1, Metadata: nil, Config: DesiredDevice{Name: "/dev/sda", OSDsPerDevice: 2, MetadataDevice: "sdb"}},
-			},
-		}
-
-		executor := &exectest.MockExecutor{}
-		executor.MockExecuteCommand = func(command string, args ...string) error {
-			logger.Infof("%s %v", command, args)
-
-			// Validate base common args
-			err := testBaseArgs(args)
-			if err != nil {
-				return err
-			}
-
-			// First command
-			if args[9] == "--osds-per-device" && args[10] == "2" && args[11] == "/dev/sda" && args[12] == "--db-devices" && args[13] == "/dev/sdb" {
-				return nil
-			}
-
-			// Second command
-			if args[9] == "--osds-per-device" && args[10] == "2" && args[11] == "/dev/sda" && args[12] == "--db-devices" && args[13] == "/dev/sdb" && args[14] == "--report" {
-				return nil
-			}
-
-			return errors.Errorf("unknown command %s %s", command, args)
-		}
-
-		executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
-			logger.Infof("%s %v", command, args)
-
-			// Validate base common args
-			err := testBaseArgs(args)
-			if err != nil {
-				return "", err
-			}
-
-			// First command
-			if args[9] == "--osds-per-device" && args[10] == "2" && args[11] == "/dev/sda" && args[12] == "--db-devices" && args[13] == "/dev/sdb" {
-				return fmt.Sprintf(`[{"block_db": "%s", "data": "%s"}, {"block_db": "%s", "data": "%s"}]`, "/dev/sdb", "/dev/sda", "/dev/sdb", "/dev/sda"), nil
-			}
-
-			return "", errors.Errorf("unknown command %s %s", command, args)
-		}
-		a := &OsdAgent{clusterInfo: &cephclient.ClusterInfo{CephVersion: cephver.CephVersion{Major: 16, Minor: 2, Extra: 0}}, nodeName: "node1", storeConfig: config.StoreConfig{StoreType: "bluestore"}}
-		context := &clusterd.Context{
-			Executor: executor,
-			Devices: []*sys.LocalDisk{
-				{Name: "sda"}, {Name: "sdb"},
-			},
-		}
-
-		err := a.initializeDevicesLVMMode(context, devices)
-		if err != nil {
-			assert.NoError(t, err, "failed metadata test")
-		} else {
-			logger.Info("success, go to next test")
-		}
-	}
-
-	// Test with metadata partition devices specified in the global OSD configuration or in the node level OSD configuration
-	{
-		devices := &DeviceOsdMapping{
-			Entries: map[string]*DeviceOsdIDEntry{
-				"sda": {Data: -1, Metadata: nil, Config: DesiredDevice{Name: "/dev/sda"}},
-				"sdc": {Data: -1, Metadata: nil, Config: DesiredDevice{Name: "/dev/sdc"}},
-			},
-		}
-
-		a := &OsdAgent{clusterInfo: &cephclient.ClusterInfo{CephVersion: cephver.CephVersion{Major: 16, Minor: 2, Extra: 0}}, nodeName: "node1", storeConfig: config.StoreConfig{StoreType: "bluestore"}, metadataDevice: "/dev/sdb1"}
-		context := &clusterd.Context{
-			Devices: []*sys.LocalDisk{
-				{Name: "sda"},
-				{Name: "sdc"},
-				{Name: "sdb1", Type: sys.PartType},
-			},
-		}
-
-		err := a.initializeDevicesLVMMode(context, devices)
-		logger.Warning(err)
-		assert.ErrorContains(t, err, "can not be specified as metadataDevice in the global OSD configuration or in the node level OSD configuration")
-	}
-
-	// Test with two devices specifies the same partition, and multiple OSDs per device
-	{
-		devices := &DeviceOsdMapping{
-			Entries: map[string]*DeviceOsdIDEntry{
-				"sda": {Data: -1, Metadata: nil, Config: DesiredDevice{Name: "/dev/sda", MetadataDevice: "sdb1"}},
-				"sdc": {Data: -1, Metadata: nil, Config: DesiredDevice{Name: "/dev/sdc", MetadataDevice: "sdb1"}},
-			},
-		}
-
-		executor := &exectest.MockExecutor{}
-		executor.MockExecuteCommand = func(command string, args ...string) error {
-			logger.Infof("%s %v", command, args)
-
-			// Validate base common args
-			err := testBasePrepareArgs(args)
-			if err != nil {
-				return err
-			}
-
-			if args[7] == "--data" && args[8] == "/dev/sda" && args[9] == "--block.db" && args[10] == "/dev/sdb1" {
-				return nil
-			}
-			if args[7] == "--data" && args[8] == "/dev/sdc" && args[9] == "--block.db" && args[10] == "/dev/sdb1" {
-				return nil
-			}
-
-			return errors.Errorf("unknown command %s %s", command, args)
-		}
-
-		a := &OsdAgent{clusterInfo: &cephclient.ClusterInfo{CephVersion: cephver.CephVersion{Major: 16, Minor: 2, Extra: 0}}, nodeName: "node1", storeConfig: config.StoreConfig{OSDsPerDevice: 3, StoreType: "bluestore"}}
-		context := &clusterd.Context{
-			Executor: executor,
-			Devices: []*sys.LocalDisk{
-				{Name: "sda"},
-				{Name: "sdc"},
-				{Name: "sdb1", Type: sys.PartType},
-			},
-		}
-
-		err := a.initializeDevicesLVMMode(context, devices)
-		if err != nil {
-			assert.NoError(t, err, "failed metadata test")
-		} else {
-			logger.Info("success, go to next test")
-		}
-	}
-
-	// Test with metadata partition devices
-	{
-		devices := &DeviceOsdMapping{
-			Entries: map[string]*DeviceOsdIDEntry{
-				"sda": {Data: -1, Metadata: nil, Config: DesiredDevice{Name: "/dev/sda", MetadataDevice: "sdb1"}},
-			},
-		}
-
-		executor := &exectest.MockExecutor{}
-		executor.MockExecuteCommand = func(command string, args ...string) error {
-			logger.Infof("%s %v", command, args)
-
-			// Validate base common args
-			err := testBasePrepareArgs(args)
-			if err != nil {
-				return err
-			}
-
-			if args[7] == "--data" && args[8] == "/dev/sda" && args[9] == "--block.db" && args[10] == "/dev/sdb1" {
-				return nil
-			}
-
-			return errors.Errorf("unknown command %s %s", command, args)
-		}
-
-		a := &OsdAgent{clusterInfo: &cephclient.ClusterInfo{CephVersion: cephver.CephVersion{Major: 16, Minor: 2, Extra: 0}}, nodeName: "node1", storeConfig: config.StoreConfig{StoreType: "bluestore"}}
-		context := &clusterd.Context{
-			Executor: executor,
-			Devices: []*sys.LocalDisk{
-				{Name: "sda"}, {Name: "sdb1", Type: sys.PartType},
-			},
-		}
-
-		err := a.initializeDevicesLVMMode(context, devices)
-		if err != nil {
-			assert.NoError(t, err, "failed metadata test")
-		} else {
-			logger.Info("success, go to next test")
-		}
-	}
-
-	// Test with two metadata partition devices, one is lvm, one is partition
-	{
-		metadataDevicePath := "/dev/test-rook-vg/test-rook-lv"
-		devices := &DeviceOsdMapping{
-			Entries: map[string]*DeviceOsdIDEntry{
-				"sda": {Data: -1, Metadata: nil, Config: DesiredDevice{Name: "/dev/sda", MetadataDevice: "sdb1"}},
-				"sdc": {Data: -1, Metadata: nil, Config: DesiredDevice{Name: "/dev/sdc", MetadataDevice: metadataDevicePath}},
-			},
-		}
-
-		executor := &exectest.MockExecutor{
-			MockExecuteCommand: func(command string, args ...string) error {
-				logger.Infof("%s %v", command, args)
-
-				var err error
-				// Validate base common args
-				if args[8] == "--yes" {
-					// lvm will use `ceph-volume lvm batch`
-					err = testBaseArgs(args)
-				} else {
-					// partition will use `ceph-volume lvm prepare`
-					err = testBasePrepareArgs(args)
-				}
-				if err != nil {
-					return err
-				}
-
-				// for partition
-				if args[7] == "--data" && args[8] == "/dev/sda" && args[9] == "--block.db" && args[10] == "/dev/sdb1" {
-					return nil
-				}
-
-				// First command for lvm
-				if args[9] == "--osds-per-device" && args[10] == "1" && args[11] == "/dev/sdc" && args[12] == "--db-devices" && args[13] == metadataDevicePath {
-					return nil
-				}
-
-				// Second command for lvm
-				if args[9] == "--osds-per-device" && args[10] == "1" && args[11] == "/dev/sdc" && args[12] == "--db-devices" && args[13] == metadataDevicePath && args[14] == "--report" {
-					return nil
-				}
-
-				return errors.Errorf("unknown command %s %s", command, args)
-			},
-
-			MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
-				logger.Infof("%s %v", command, args)
-
-				// Validate base common args
-				err := testBaseArgs(args)
-				if err != nil {
-					return "", err
-				}
-
-				// First command
-				if args[9] == "--osds-per-device" && args[10] == "1" && args[11] == "/dev/sdc" && args[12] == "--db-devices" && args[13] == metadataDevicePath {
-					return fmt.Sprintf(`[{"block_db": "%s", "data": "%s"}]`, metadataDevicePath, "/dev/sdc"), nil
-				}
-
-				return "", errors.Errorf("unknown command %s %s", command, args)
-			},
-		}
-
-		a := &OsdAgent{clusterInfo: &cephclient.ClusterInfo{CephVersion: cephver.CephVersion{Major: 16, Minor: 2, Extra: 0}}, nodeName: "node1", storeConfig: config.StoreConfig{StoreType: "bluestore"}}
-		context := &clusterd.Context{
-			Executor: executor,
-			Devices: []*sys.LocalDisk{
-				{Name: "sda"},
-				{Name: "sdb1", Type: sys.PartType},
-				{
-					Name:     "sdc",
-					Type:     "disk",
-					DevLinks: "/dev/disk/by-id/wwn-0x6f4ee080051fd00029bb505f1df6ee3a /dev/disk/by-path/pci-0000:3b:00.0-scsi-0:2:0:0",
-				},
-				{
-					Name:     "vg-test-rook-lv",
-					Type:     "lvm",
-					DevLinks: "/dev/mapper/test--rook--vg-test--rook--lv /dev/test-rook-vg/test-rook-lv",
-				},
-			},
-		}
-
-		err := a.initializeDevicesLVMMode(context, devices)
-		if err != nil {
-			assert.NoError(t, err, "failed metadata test")
-		} else {
-			logger.Info("success, go to next test")
-		}
-	}
-
 	// Test with metadata devices with dev by-id
 	{
 		metadataDeviceByIDPath := "/dev/disk/by-id/nvme-Samsung_SSD_970_EVO_Plus_1TB_XXX"
@@ -1180,58 +906,6 @@ func TestInitializeBlock(t *testing.T) {
 		}
 	}
 
-	// Test with metadata partition devices with dev by-id
-	{
-		metadataDeviceByIDPath := "/dev/disk/by-id/nvme-BC511_NVMe_SK_hynix_512GB_CD08N413611008838-part1"
-		metadataDevicePath := "/dev/nvme0n1p1"
-		devices := &DeviceOsdMapping{
-			Entries: map[string]*DeviceOsdIDEntry{
-				"sda": {Data: -1, Metadata: nil, Config: DesiredDevice{Name: "/dev/sda", MetadataDevice: metadataDeviceByIDPath}},
-			},
-		}
-
-		executor := &exectest.MockExecutor{}
-		executor.MockExecuteCommand = func(command string, args ...string) error {
-			logger.Infof("%s %v", command, args)
-
-			// Validate base common args
-			err := testBasePrepareArgs(args)
-			if err != nil {
-				return err
-			}
-
-			if args[7] == "--data" && args[8] == "/dev/sda" && args[9] == "--block.db" && args[10] == metadataDevicePath {
-				return nil
-			}
-
-			return errors.Errorf("unknown command %s %s", command, args)
-		}
-
-		a := &OsdAgent{clusterInfo: &cephclient.ClusterInfo{CephVersion: cephver.CephVersion{Major: 16, Minor: 2, Extra: 0}}, nodeName: "node1", storeConfig: config.StoreConfig{StoreType: "bluestore"}}
-		context := &clusterd.Context{
-			Executor: executor,
-			Devices: []*sys.LocalDisk{
-				{
-					Name:     "sda",
-					Type:     "disk",
-					DevLinks: "/dev/disk/by-id/wwn-0x6f4ee080051fd00029bb505f1df6ee3a /dev/disk/by-path/pci-0000:3b:00.0-scsi-0:2:0:0",
-				},
-				{
-					Name:     "nvme0n1p1",
-					Type:     "part",
-					DevLinks: "/dev/disk/by-id/nvme-BC511_NVMe_SK_hynix_512GB_CD08N413611008838-part1 /dev/disk/by-partuuid/7cf003d3-3a56-4011-b736-b5a741b0aabc",
-				},
-			},
-		}
-
-		err := a.initializeDevicesLVMMode(context, devices)
-		if err != nil {
-			assert.NoError(t, err, "failed metadata partition device by-id test")
-		} else {
-			logger.Info("success, go to next test")
-		}
-	}
-
 	// Test with metadata devices with dev by-path
 	{
 		devices := &DeviceOsdMapping{
@@ -1314,66 +988,6 @@ func TestInitializeBlock(t *testing.T) {
 		logger.Info("success, go to next test")
 	}
 
-	// Test with metadata partition devices with dev by-path
-	{
-		devices := &DeviceOsdMapping{
-			Entries: map[string]*DeviceOsdIDEntry{
-				"sda": {
-					Data:     -1,
-					Metadata: nil,
-					Config: DesiredDevice{
-						Name:           "/dev/sda",
-						MetadataDevice: "/dev/disk/by-path/pci-0000:3a:00.0-nvme-1-part1",
-					},
-				},
-			},
-		}
-		metadataDevicePath := "/dev/nvme0n1p1"
-
-		executor := &exectest.MockExecutor{}
-		executor.MockExecuteCommand = func(command string, args ...string) error {
-			logger.Infof("%s %v", command, args)
-
-			// Validate base common args
-			err := testBasePrepareArgs(args)
-			if err != nil {
-				return err
-			}
-
-			if args[7] == "--data" && args[8] == "/dev/sda" && args[9] == "--block.db" && args[10] == metadataDevicePath {
-				return nil
-			}
-
-			return errors.Errorf("unknown command %s %s", command, args)
-		}
-
-		agent := &OsdAgent{
-			clusterInfo: &cephclient.ClusterInfo{
-				CephVersion: cephver.CephVersion{Major: 16, Minor: 2, Extra: 0},
-			},
-			nodeName:    "node1",
-			storeConfig: config.StoreConfig{StoreType: "bluestore"},
-		}
-		context := &clusterd.Context{Executor: executor,
-			Devices: []*sys.LocalDisk{
-				{
-					Name:     "sda",
-					Type:     "disk",
-					DevLinks: "/dev/disk/by-id/wwn-0x6f4ee080051fd00029bb505f1df6ee3a /dev/disk/by-path/pci-0000:3b:00.0-scsi-0:2:0:0",
-				},
-				{
-					Name:     "nvme0n1p1",
-					Type:     "part",
-					DevLinks: "/dev/disk/by-path/pci-0000:3a:00.0-nvme-1-part1 /dev/disk/by-id/nvme-BC511_NVMe_SK_hynix_512GB_CD08N413611008838-part1 /dev/disk/by-partuuid/7cf003d3-3a56-4011-b736-b5a741b0aabc",
-				},
-			},
-		}
-
-		err := agent.initializeDevicesLVMMode(context, devices)
-		assert.NoError(t, err, "failed metadata device by-path test")
-		logger.Info("success, go to next test")
-	}
-
 	// Test with metadata devices with lvm
 	{
 		metadataDevicePath := "/dev/test-rook-vg/test-rook-lv"
@@ -1444,7 +1058,7 @@ func TestInitializeBlock(t *testing.T) {
 					DevLinks: "/dev/disk/by-id/wwn-0x6f4ee080051fd00029bb505f1df6ee3a /dev/disk/by-path/pci-0000:3b:00.0-scsi-0:2:0:0",
 				},
 				{
-					Name:     "vg-test-rook-lv",
+					Name:     "nvme0n1",
 					Type:     "lvm",
 					DevLinks: "/dev/mapper/test--rook--vg-test--rook--lv /dev/test-rook-vg/test-rook-lv",
 				},
@@ -1585,8 +1199,7 @@ func TestInitializeBlockPVC(t *testing.T) {
 	}
 	executor.MockExecuteCommandWithCombinedOutput = func(command string, args ...string) (string, error) {
 		logger.Infof("%s %v", command, args)
-		if args[1] == "ceph-volume" && args[2] == "raw" && args[3] == "prepare" && args[4] == "--bluestore" && args[7] == "--osd-id" && args[8] == "3" ||
-			args[1] == "ceph-volume" && args[4] == "raw" && args[5] == "prepare" && args[6] == "--bluestore" && args[9] == "--osd-id" && args[10] == "3" {
+		if args[1] == "ceph-volume" && args[2] == "raw" && args[3] == "prepare" && args[4] == "--bluestore" && args[7] == "--osd-id" && args[8] == "3" {
 			return initializeBlockPVCTestResult, nil
 		}
 
@@ -1607,8 +1220,7 @@ func TestInitializeBlockPVC(t *testing.T) {
 	}
 	executor.MockExecuteCommandWithCombinedOutput = func(command string, args ...string) (string, error) {
 		logger.Infof("%s %v", command, args)
-		if args[1] == "ceph-volume" && args[2] == "raw" && args[3] == "prepare" && args[4] == "--bluestore" && args[7] != "--osd-id" && args[8] != "3" ||
-			args[1] == "ceph-volume" && args[4] == "raw" && args[5] == "prepare" && args[6] == "--bluestore" && args[9] != "--osd-id" && args[10] != "3" {
+		if args[1] == "ceph-volume" && args[2] == "raw" && args[3] == "prepare" && args[4] == "--bluestore" && args[7] != "--osd-id" && args[8] != "3" {
 			return initializeBlockPVCTestResult, nil
 		}
 
diff --git a/pkg/operator/ceph/csi/util_test.go b/pkg/operator/ceph/csi/util_test.go
index 1ee73d58c..66ba19e74 100644
--- a/pkg/operator/ceph/csi/util_test.go
+++ b/pkg/operator/ceph/csi/util_test.go
@@ -267,7 +267,7 @@ func Test_getImage(t *testing.T) {
 			args: args{
 				data:         map[string]string{},
 				settingName:  "ROOK_CSI_CEPH_IMAGE",
-				defaultImage: "quay.io/cephcsi/cephcsi:v3.10.2",
+				defaultImage: "quay.io/cephcsi/cephcsi:v3.10.1",
 			},
 			want: DefaultCSIPluginImage,
 		},
@@ -278,7 +278,7 @@ func Test_getImage(t *testing.T) {
 					"ROOK_CSI_CEPH_IMAGE": "registry.io/private/cephcsi:v8",
 				},
 				settingName:  "ROOK_CSI_CEPH_IMAGE",
-				defaultImage: "quay.io/cephcsi/cephcsi:v3.10.2",
+				defaultImage: "quay.io/cephcsi/cephcsi:v3.10.1",
 			},
 			want: "registry.io/private/cephcsi:v8",
 		},
@@ -289,9 +289,9 @@ func Test_getImage(t *testing.T) {
 					"ROOK_CSI_CEPH_IMAGE": "registry.io/private/cephcsi",
 				},
 				settingName:  "ROOK_CSI_CEPH_IMAGE",
-				defaultImage: "quay.io/cephcsi/cephcsi:v3.10.2",
+				defaultImage: "quay.io/cephcsi/cephcsi:v3.10.1",
 			},
-			want: "registry.io/private/cephcsi:v3.10.2",
+			want: "registry.io/private/cephcsi:v3.10.1",
 		},
 	}
 	for _, tt := range tests {
diff --git a/pkg/operator/ceph/csi/version_test.go b/pkg/operator/ceph/csi/version_test.go
index 8877104fc..cf2fa2eb7 100644
--- a/pkg/operator/ceph/csi/version_test.go
+++ b/pkg/operator/ceph/csi/version_test.go
@@ -30,7 +30,6 @@ var (
 	testReleaseV391  = CephCSIVersion{3, 9, 1}
 	testreleasev310  = CephCSIVersion{3, 10, 0}
 	testReleaseV3101 = CephCSIVersion{3, 10, 1}
-	testReleaseV3102 = CephCSIVersion{3, 10, 2}
 
 	testVersionUnsupported = CephCSIVersion{4, 0, 0}
 )
@@ -61,10 +60,6 @@ func TestIsAtLeast(t *testing.T) {
 	ret = testReleaseV3101.isAtLeast(&testReleaseV3101)
 	assert.Equal(t, true, ret)
 
-	// Test for 3.10.2
-	ret = testReleaseV3102.isAtLeast(&testReleaseV3102)
-	assert.Equal(t, true, ret)
-
 }
 
 func TestSupported(t *testing.T) {
diff --git a/tests/scripts/github-action-helper.sh b/tests/scripts/github-action-helper.sh
index c7b3ae0d2..aa97801eb 100755
--- a/tests/scripts/github-action-helper.sh
+++ b/tests/scripts/github-action-helper.sh
@@ -280,9 +280,6 @@ function deploy_cluster() {
     sed -i "s|#deviceFilter:|deviceFilter: ${BLOCK/\/dev\//}\n    config:\n      osdsPerDevice: \"2\"|g" cluster-test.yaml
   elif [ "$1" = "osd_with_metadata_device" ]; then
     sed -i "s|#deviceFilter:|deviceFilter: ${BLOCK/\/dev\//}\n    config:\n      metadataDevice: /dev/test-rook-vg/test-rook-lv|g" cluster-test.yaml
-  elif [ "$1" = "osd_with_metadata_partition_device" ]; then
-    yq w -i -d0 cluster-test.yaml spec.storage.devices[0].name ${BLOCK}2
-    yq w -i -d0 cluster-test.yaml spec.storage.devices[0].config.metadataDevice ${BLOCK}1
   elif [ "$1" = "encryption" ]; then
     sed -i "s|#deviceFilter:|deviceFilter: ${BLOCK/\/dev\//}\n    config:\n      encryptedDevice: \"true\"|g" cluster-test.yaml
   elif [ "$1" = "lvm" ]; then