This patch added four functions: tcmu_pr_info_clear_reg() would
delete a registration from a struct tcmu_pr_info,
tcmu_pr_info_rsv_clear() would clear the reservation,
tcmu_is_rsv_holder() would check whether the registration is a
reservation holder. Then tcmu_pr_info_unregister_reg() would
call the functions above to unregister the registration.

Signed-off-by: Zhu Lingshan <[email protected]>
---
 drivers/target/target_core_user.c | 75 +++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/drivers/target/target_core_user.c 
b/drivers/target/target_core_user.c
index 5d0da54072cd..a6e951f04065 100644
--- a/drivers/target/target_core_user.c
+++ b/drivers/target/target_core_user.c
@@ -41,6 +41,7 @@
 #include <target/target_core_backend.h>
 
 #include <linux/target_core_user.h>
+#include "target_core_pr.h"
 
 /*
  * Define a shared-memory interface for LIO to pass SCSI commands and
@@ -2539,6 +2540,80 @@ tcmu_execute_pr_register_new(struct tcmu_pr_info 
*pr_info, u64 old_key,
        return ret;
 }
 
+static bool tcmu_is_rsv_holder(struct tcmu_pr_rsv *rsv,
+                              struct tcmu_pr_reg *reg, bool *rsv_is_all_reg)
+{
+       if (!rsv || !reg) {
+               WARN_ON(1);
+               return false;
+       }
+       if ((rsv->type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG)
+           || (rsv->type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG)) {
+               /* any registeration is a reservation holder */
+               if (rsv_is_all_reg)
+                       *rsv_is_all_reg = true;
+               return true;
+       }
+
+       if (rsv_is_all_reg)
+               *rsv_is_all_reg = false;
+
+       if ((rsv->key == reg->key)
+           && !strncmp(rsv->it_nexus, reg->it_nexus,
+                       ARRAY_SIZE(rsv->it_nexus))) {
+               return true;
+       }
+
+       return false;
+}
+
+static void tcmu_pr_info_clear_reg(struct tcmu_pr_info *pr_info,
+                                  struct tcmu_pr_reg *reg)
+{
+       list_del(&reg->regs_node);
+       pr_info->num_regs--;
+
+       pr_debug("deleted pr_info reg: 0x%llx\n", reg->key);
+
+       kfree(reg);
+}
+
+static void tcmu_pr_info_rsv_clear(struct tcmu_pr_info *pr_info)
+{
+       kfree(pr_info->rsv);
+       pr_info->rsv = NULL;
+
+       pr_debug("pr_info rsv cleared\n");
+}
+
+static int tcmu_pr_info_unregister_reg(struct tcmu_pr_info *pr_info,
+                                      struct tcmu_pr_reg *reg)
+{
+       struct tcmu_pr_rsv *rsv;
+       bool all_reg = false;
+
+       rsv = pr_info->rsv;
+       if (rsv && tcmu_is_rsv_holder(rsv, reg, &all_reg)) {
+               /*
+                * If the persistent reservation holder is more than one I_T
+                * nexus, the reservation shall not be released until the
+                * registrations for all persistent reservation holder I_T
+                * nexuses are removed.
+                */
+               if (!all_reg || (pr_info->num_regs == 1)) {
+                       pr_warn("implicitly releasing PR of type %d",
+                               rsv->type);
+                       pr_warn("on unregister I_T Nexus %s\n",
+                               reg->it_nexus);
+                       tcmu_pr_info_rsv_clear(pr_info);
+               }
+       }
+
+       tcmu_pr_info_clear_reg(pr_info, reg);
+
+       return 0;
+}
+
 static int tcmu_configure_device(struct se_device *dev)
 {
        struct tcmu_dev *udev = TCMU_DEV(dev);
-- 
2.17.1

Reply via email to