|
|
f07e16 |
From 575d2ac72342beea5c3d63fc655d5d173b01283a Mon Sep 17 00:00:00 2001
|
|
|
f07e16 |
From: Artem Savkov <asavkov@redhat.com>
|
|
|
f07e16 |
Date: Tue, 2 Feb 2021 11:12:30 +0100
|
|
|
f07e16 |
Subject: [RHEL7.9 KPATCH v2] [target] scsi: Fix XCOPY NAA identifier lookup
|
|
|
f07e16 |
|
|
|
f07e16 |
Kernels:
|
|
|
f07e16 |
3.10.0-1160.el7
|
|
|
f07e16 |
3.10.0-1160.2.1.el7
|
|
|
f07e16 |
3.10.0-1160.2.2.el7
|
|
|
f07e16 |
3.10.0-1160.6.1.el7
|
|
|
f07e16 |
3.10.0-1160.11.1.el7
|
|
|
f07e16 |
|
|
|
f07e16 |
Changes since last build:
|
|
|
f07e16 |
[x86_64]:
|
|
|
f07e16 |
target_core_xcopy.o: changed function: target_do_xcopy
|
|
|
f07e16 |
target_core_xcopy.o: changed function: target_parse_xcopy_cmd
|
|
|
f07e16 |
target_core_xcopy.o: changed function: target_xcopy_do_work
|
|
|
f07e16 |
target_core_xcopy.o: changed function: target_xcopy_locate_se_dev_e4
|
|
|
f07e16 |
target_core_xcopy.o: changed function: xcopy_pt_undepend_remotedev
|
|
|
f07e16 |
|
|
|
f07e16 |
[ppc64le]:
|
|
|
f07e16 |
target_core_xcopy.o: changed function: target_do_receive_copy_results
|
|
|
f07e16 |
target_core_xcopy.o: changed function: target_do_xcopy
|
|
|
f07e16 |
target_core_xcopy.o: changed function: target_parse_xcopy_cmd
|
|
|
f07e16 |
target_core_xcopy.o: changed function: target_xcopy_do_work
|
|
|
f07e16 |
target_core_xcopy.o: new function: target_xcopy_locate_se_dev_e4
|
|
|
f07e16 |
target_core_xcopy.o: new function: xcopy_pt_undepend_remotedev
|
|
|
f07e16 |
|
|
|
f07e16 |
---------------------------
|
|
|
f07e16 |
|
|
|
f07e16 |
Modifications: shadow var instead of structure fixup
|
|
|
f07e16 |
|
|
|
f07e16 |
commit 173ce8ce7c0c334c3406b4826dca6732f101dd2e
|
|
|
f07e16 |
Author: Maurizio Lombardi <mlombard@redhat.com>
|
|
|
f07e16 |
Date: Mon Jan 18 16:24:30 2021 -0500
|
|
|
f07e16 |
|
|
|
f07e16 |
[target] scsi: Fix XCOPY NAA identifier lookup
|
|
|
f07e16 |
|
|
|
f07e16 |
Message-id: <20210118162431.74459-1-mlombard@redhat.com>
|
|
|
f07e16 |
Patchwork-id: 10208
|
|
|
f07e16 |
Patchwork-instance: patchwork-private
|
|
|
f07e16 |
O-Subject: [kernel team] [CVE-2020-28374 RHEL7.9.z e-stor PATCH] scsi: target: Fix XCOPY NAA identifier lookup
|
|
|
f07e16 |
Bugzilla: 1900469
|
|
|
f07e16 |
CVE: CVE-2020-28374
|
|
|
f07e16 |
RH-Acked-by: Tomas Henzl <thenzl@redhat.com>
|
|
|
f07e16 |
RH-Acked-by: Ewan D. Milne <emilne@redhat.com>
|
|
|
f07e16 |
|
|
|
f07e16 |
From: David Disseldorp <ddiss@suse.de>
|
|
|
f07e16 |
|
|
|
f07e16 |
BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1900469
|
|
|
f07e16 |
Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=34294843
|
|
|
f07e16 |
Upstream: cherry-picked from the mainline tree
|
|
|
f07e16 |
|
|
|
f07e16 |
When attempting to match EXTENDED COPY CSCD descriptors with corresponding
|
|
|
f07e16 |
se_devices, target_xcopy_locate_se_dev_e4() currently iterates over LIO's
|
|
|
f07e16 |
global devices list which includes all configured backstores.
|
|
|
f07e16 |
|
|
|
f07e16 |
This change ensures that only initiator-accessible backstores are
|
|
|
f07e16 |
considered during CSCD descriptor lookup, according to the session's
|
|
|
f07e16 |
se_node_acl LUN list.
|
|
|
f07e16 |
|
|
|
f07e16 |
To avoid LUN removal race conditions, device pinning is changed from being
|
|
|
f07e16 |
configfs based to instead using the se_node_acl lun_ref.
|
|
|
f07e16 |
|
|
|
f07e16 |
Reference: CVE-2020-28374
|
|
|
f07e16 |
Fixes: cbf031f425fd ("target: Add support for EXTENDED_COPY copy offload emulation")
|
|
|
f07e16 |
Reviewed-by: Lee Duncan <lduncan@suse.com>
|
|
|
f07e16 |
Signed-off-by: David Disseldorp <ddiss@suse.de>
|
|
|
f07e16 |
Signed-off-by: Mike Christie <michael.christie@oracle.com>
|
|
|
f07e16 |
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
|
|
|
f07e16 |
(cherry picked from commit 2896c93811e39d63a4d9b63ccf12a8fbc226e5e4)
|
|
|
f07e16 |
Signed-off-by: Maurizio Lombardi <mlombard@redhat.com>
|
|
|
f07e16 |
Signed-off-by: Augusto Caringi <acaringi@redhat.com>
|
|
|
f07e16 |
|
|
|
f07e16 |
Signed-off-by: Artem Savkov <asavkov@redhat.com>
|
|
|
f07e16 |
Acked-by: Joe Lawrence <joe.lawrence@redhat.com>
|
|
|
f07e16 |
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
|
|
|
f07e16 |
---
|
|
|
f07e16 |
drivers/target/target_core_xcopy.c | 143 +++++++++++++++++++----------
|
|
|
f07e16 |
1 file changed, 95 insertions(+), 48 deletions(-)
|
|
|
f07e16 |
|
|
|
f07e16 |
diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c
|
|
|
f07e16 |
index 252de556dbb3..4d023e403845 100644
|
|
|
f07e16 |
--- a/drivers/target/target_core_xcopy.c
|
|
|
f07e16 |
+++ b/drivers/target/target_core_xcopy.c
|
|
|
f07e16 |
@@ -38,6 +38,8 @@
|
|
|
f07e16 |
#include "target_core_ua.h"
|
|
|
f07e16 |
#include "target_core_xcopy.h"
|
|
|
f07e16 |
|
|
|
f07e16 |
+#define KLP_SHADOW_REMOTE_LUN_REF 0x2020283740000000
|
|
|
f07e16 |
+
|
|
|
f07e16 |
static struct workqueue_struct *xcopy_wq = NULL;
|
|
|
f07e16 |
|
|
|
f07e16 |
static sense_reason_t target_parse_xcopy_cmd(struct xcopy_op *xop);
|
|
|
f07e16 |
@@ -55,60 +57,83 @@ static int target_xcopy_gen_naa_ieee(struct se_device *dev, unsigned char *buf)
|
|
|
f07e16 |
return 0;
|
|
|
f07e16 |
}
|
|
|
f07e16 |
|
|
|
f07e16 |
-struct xcopy_dev_search_info {
|
|
|
f07e16 |
- const unsigned char *dev_wwn;
|
|
|
f07e16 |
- struct se_device *found_dev;
|
|
|
f07e16 |
-};
|
|
|
f07e16 |
-
|
|
|
f07e16 |
+/**
|
|
|
f07e16 |
+ * target_xcopy_locate_se_dev_e4_iter - compare XCOPY NAA device identifiers
|
|
|
f07e16 |
+ *
|
|
|
f07e16 |
+ * @se_dev: device being considered for match
|
|
|
f07e16 |
+ * @dev_wwn: XCOPY requested NAA dev_wwn
|
|
|
f07e16 |
+ * @return: 1 on match, 0 on no-match
|
|
|
f07e16 |
+ */
|
|
|
f07e16 |
static int target_xcopy_locate_se_dev_e4_iter(struct se_device *se_dev,
|
|
|
f07e16 |
- void *data)
|
|
|
f07e16 |
+ const unsigned char *dev_wwn)
|
|
|
f07e16 |
{
|
|
|
f07e16 |
- struct xcopy_dev_search_info *info = data;
|
|
|
f07e16 |
unsigned char tmp_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN];
|
|
|
f07e16 |
int rc;
|
|
|
f07e16 |
|
|
|
f07e16 |
- if (!se_dev->dev_attrib.emulate_3pc)
|
|
|
f07e16 |
+ if (!se_dev->dev_attrib.emulate_3pc) {
|
|
|
f07e16 |
+ pr_debug("XCOPY: emulate_3pc disabled on se_dev %p\n", se_dev);
|
|
|
f07e16 |
return 0;
|
|
|
f07e16 |
+ }
|
|
|
f07e16 |
|
|
|
f07e16 |
memset(&tmp_dev_wwn[0], 0, XCOPY_NAA_IEEE_REGEX_LEN);
|
|
|
f07e16 |
target_xcopy_gen_naa_ieee(se_dev, &tmp_dev_wwn[0]);
|
|
|
f07e16 |
|
|
|
f07e16 |
- rc = memcmp(&tmp_dev_wwn[0], info->dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN);
|
|
|
f07e16 |
- if (rc != 0)
|
|
|
f07e16 |
- return 0;
|
|
|
f07e16 |
-
|
|
|
f07e16 |
- info->found_dev = se_dev;
|
|
|
f07e16 |
- pr_debug("XCOPY 0xe4: located se_dev: %p\n", se_dev);
|
|
|
f07e16 |
-
|
|
|
f07e16 |
- rc = target_depend_item(&se_dev->dev_group.cg_item);
|
|
|
f07e16 |
+ rc = memcmp(&tmp_dev_wwn[0], dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN);
|
|
|
f07e16 |
if (rc != 0) {
|
|
|
f07e16 |
- pr_err("configfs_depend_item attempt failed: %d for se_dev: %p\n",
|
|
|
f07e16 |
- rc, se_dev);
|
|
|
f07e16 |
- return rc;
|
|
|
f07e16 |
+ pr_debug("XCOPY: skip non-matching: %*ph\n",
|
|
|
f07e16 |
+ XCOPY_NAA_IEEE_REGEX_LEN, tmp_dev_wwn);
|
|
|
f07e16 |
+ return 0;
|
|
|
f07e16 |
}
|
|
|
f07e16 |
+ pr_debug("XCOPY 0xe4: located se_dev: %p\n", se_dev);
|
|
|
f07e16 |
|
|
|
f07e16 |
- pr_debug("Called configfs_depend_item for se_dev: %p se_dev->se_dev_group: %p\n",
|
|
|
f07e16 |
- se_dev, &se_dev->dev_group);
|
|
|
f07e16 |
return 1;
|
|
|
f07e16 |
}
|
|
|
f07e16 |
|
|
|
f07e16 |
-static int target_xcopy_locate_se_dev_e4(const unsigned char *dev_wwn,
|
|
|
f07e16 |
- struct se_device **found_dev)
|
|
|
f07e16 |
+static int target_xcopy_locate_se_dev_e4(struct se_session *sess,
|
|
|
f07e16 |
+ const unsigned char *dev_wwn,
|
|
|
f07e16 |
+ struct se_device **_found_dev,
|
|
|
f07e16 |
+ struct percpu_ref **_found_lun_ref)
|
|
|
f07e16 |
{
|
|
|
f07e16 |
- struct xcopy_dev_search_info info;
|
|
|
f07e16 |
- int ret;
|
|
|
f07e16 |
-
|
|
|
f07e16 |
- memset(&info, 0, sizeof(info));
|
|
|
f07e16 |
- info.dev_wwn = dev_wwn;
|
|
|
f07e16 |
-
|
|
|
f07e16 |
- ret = target_for_each_device(target_xcopy_locate_se_dev_e4_iter, &info;;
|
|
|
f07e16 |
- if (ret == 1) {
|
|
|
f07e16 |
- *found_dev = info.found_dev;
|
|
|
f07e16 |
- return 0;
|
|
|
f07e16 |
- } else {
|
|
|
f07e16 |
- pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
|
|
|
f07e16 |
- return -EINVAL;
|
|
|
f07e16 |
+ struct se_dev_entry *deve;
|
|
|
f07e16 |
+ struct se_node_acl *nacl;
|
|
|
f07e16 |
+ struct se_lun *this_lun = NULL;
|
|
|
f07e16 |
+ struct se_device *found_dev = NULL;
|
|
|
f07e16 |
+
|
|
|
f07e16 |
+ /* cmd with NULL sess indicates no associated $FABRIC_MOD */
|
|
|
f07e16 |
+ if (!sess)
|
|
|
f07e16 |
+ goto err_out;
|
|
|
f07e16 |
+
|
|
|
f07e16 |
+ pr_debug("XCOPY 0xe4: searching for: %*ph\n",
|
|
|
f07e16 |
+ XCOPY_NAA_IEEE_REGEX_LEN, dev_wwn);
|
|
|
f07e16 |
+
|
|
|
f07e16 |
+ nacl = sess->se_node_acl;
|
|
|
f07e16 |
+ rcu_read_lock();
|
|
|
f07e16 |
+ hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) {
|
|
|
f07e16 |
+ struct se_device *this_dev;
|
|
|
f07e16 |
+ int rc;
|
|
|
f07e16 |
+
|
|
|
f07e16 |
+ this_lun = rcu_dereference(deve->se_lun);
|
|
|
f07e16 |
+ this_dev = rcu_dereference_raw(this_lun->lun_se_dev);
|
|
|
f07e16 |
+
|
|
|
f07e16 |
+ rc = target_xcopy_locate_se_dev_e4_iter(this_dev, dev_wwn);
|
|
|
f07e16 |
+ if (rc) {
|
|
|
f07e16 |
+ if (percpu_ref_tryget_live(&this_lun->lun_ref))
|
|
|
f07e16 |
+ found_dev = this_dev;
|
|
|
f07e16 |
+ break;
|
|
|
f07e16 |
+ }
|
|
|
f07e16 |
}
|
|
|
f07e16 |
+ rcu_read_unlock();
|
|
|
f07e16 |
+ if (found_dev == NULL)
|
|
|
f07e16 |
+ goto err_out;
|
|
|
f07e16 |
+
|
|
|
f07e16 |
+ pr_debug("lun_ref held for se_dev: %p se_dev->se_dev_group: %p\n",
|
|
|
f07e16 |
+ found_dev, &found_dev->dev_group);
|
|
|
f07e16 |
+ *_found_dev = found_dev;
|
|
|
f07e16 |
+ *_found_lun_ref = &this_lun->lun_ref;
|
|
|
f07e16 |
+ return 0;
|
|
|
f07e16 |
+err_out:
|
|
|
f07e16 |
+ pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
|
|
|
f07e16 |
+ return -EINVAL;
|
|
|
f07e16 |
}
|
|
|
f07e16 |
|
|
|
f07e16 |
static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op *xop,
|
|
|
f07e16 |
@@ -197,6 +222,8 @@ static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op
|
|
|
f07e16 |
return 0;
|
|
|
f07e16 |
}
|
|
|
f07e16 |
|
|
|
f07e16 |
+#include "kpatch-macros.h"
|
|
|
f07e16 |
+
|
|
|
f07e16 |
static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
|
|
|
f07e16 |
struct xcopy_op *xop, unsigned char *p,
|
|
|
f07e16 |
unsigned short tdll, sense_reason_t *sense_ret)
|
|
|
f07e16 |
@@ -206,6 +233,7 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
|
|
|
f07e16 |
int offset = tdll % XCOPY_TARGET_DESC_LEN, rc;
|
|
|
f07e16 |
unsigned short cscd_index = 0;
|
|
|
f07e16 |
unsigned short start = 0;
|
|
|
f07e16 |
+ struct percpu_ref **remote_lun_ref;
|
|
|
f07e16 |
|
|
|
f07e16 |
*sense_ret = TCM_INVALID_PARAMETER_LIST;
|
|
|
f07e16 |
|
|
|
f07e16 |
@@ -253,14 +281,24 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
|
|
|
f07e16 |
}
|
|
|
f07e16 |
}
|
|
|
f07e16 |
|
|
|
f07e16 |
+ remote_lun_ref = klp_shadow_get_or_alloc(xop, KLP_SHADOW_REMOTE_LUN_REF,
|
|
|
f07e16 |
+ sizeof(struct percpu_ref*), GFP_KERNEL, NULL, NULL);
|
|
|
f07e16 |
+
|
|
|
f07e16 |
+ if (!remote_lun_ref)
|
|
|
f07e16 |
+ goto out;
|
|
|
f07e16 |
+
|
|
|
f07e16 |
switch (xop->op_origin) {
|
|
|
f07e16 |
case XCOL_SOURCE_RECV_OP:
|
|
|
f07e16 |
- rc = target_xcopy_locate_se_dev_e4(xop->dst_tid_wwn,
|
|
|
f07e16 |
- &xop->dst_dev);
|
|
|
f07e16 |
+ rc = target_xcopy_locate_se_dev_e4(se_cmd->se_sess,
|
|
|
f07e16 |
+ xop->dst_tid_wwn,
|
|
|
f07e16 |
+ &xop->dst_dev,
|
|
|
f07e16 |
+ remote_lun_ref);
|
|
|
f07e16 |
break;
|
|
|
f07e16 |
case XCOL_DEST_RECV_OP:
|
|
|
f07e16 |
- rc = target_xcopy_locate_se_dev_e4(xop->src_tid_wwn,
|
|
|
f07e16 |
- &xop->src_dev);
|
|
|
f07e16 |
+ rc = target_xcopy_locate_se_dev_e4(se_cmd->se_sess,
|
|
|
f07e16 |
+ xop->src_tid_wwn,
|
|
|
f07e16 |
+ &xop->src_dev,
|
|
|
f07e16 |
+ remote_lun_ref);
|
|
|
f07e16 |
break;
|
|
|
f07e16 |
default:
|
|
|
f07e16 |
pr_err("XCOPY CSCD descriptor IDs not found in CSCD list - "
|
|
|
f07e16 |
@@ -406,18 +444,16 @@ static int xcopy_pt_get_cmd_state(struct se_cmd *se_cmd)
|
|
|
f07e16 |
|
|
|
f07e16 |
static void xcopy_pt_undepend_remotedev(struct xcopy_op *xop)
|
|
|
f07e16 |
{
|
|
|
f07e16 |
- struct se_device *remote_dev;
|
|
|
f07e16 |
+ struct percpu_ref **remote_lun_ref = klp_shadow_get(xop,
|
|
|
f07e16 |
+ KLP_SHADOW_REMOTE_LUN_REF);
|
|
|
f07e16 |
|
|
|
f07e16 |
if (xop->op_origin == XCOL_SOURCE_RECV_OP)
|
|
|
f07e16 |
- remote_dev = xop->dst_dev;
|
|
|
f07e16 |
+ pr_debug("putting dst lun_ref for %p\n", xop->dst_dev);
|
|
|
f07e16 |
else
|
|
|
f07e16 |
- remote_dev = xop->src_dev;
|
|
|
f07e16 |
+ pr_debug("putting src lun_ref for %p\n", xop->src_dev);
|
|
|
f07e16 |
|
|
|
f07e16 |
- pr_debug("Calling configfs_undepend_item for"
|
|
|
f07e16 |
- " remote_dev: %p remote_dev->dev_group: %p\n",
|
|
|
f07e16 |
- remote_dev, &remote_dev->dev_group.cg_item);
|
|
|
f07e16 |
-
|
|
|
f07e16 |
- target_undepend_item(&remote_dev->dev_group.cg_item);
|
|
|
f07e16 |
+ if (remote_lun_ref)
|
|
|
f07e16 |
+ percpu_ref_put(*remote_lun_ref);
|
|
|
f07e16 |
}
|
|
|
f07e16 |
|
|
|
f07e16 |
static void xcopy_pt_release_cmd(struct se_cmd *se_cmd)
|
|
|
f07e16 |
@@ -857,6 +893,7 @@ static void target_xcopy_do_work(struct work_struct *work)
|
|
|
f07e16 |
}
|
|
|
f07e16 |
|
|
|
f07e16 |
xcopy_pt_undepend_remotedev(xop);
|
|
|
f07e16 |
+ klp_shadow_free(xop, KLP_SHADOW_REMOTE_LUN_REF, NULL);
|
|
|
f07e16 |
kfree(xop);
|
|
|
f07e16 |
|
|
|
f07e16 |
pr_debug("target_xcopy_do_work: Final src_lba: %llu, dst_lba: %llu\n",
|
|
|
f07e16 |
@@ -872,6 +909,7 @@ out:
|
|
|
f07e16 |
xcopy_pt_undepend_remotedev(xop);
|
|
|
f07e16 |
|
|
|
f07e16 |
err_free:
|
|
|
f07e16 |
+ klp_shadow_free(xop, KLP_SHADOW_REMOTE_LUN_REF, NULL);
|
|
|
f07e16 |
kfree(xop);
|
|
|
f07e16 |
/*
|
|
|
f07e16 |
* Don't override an error scsi status if it has already been set
|
|
|
f07e16 |
@@ -981,6 +1019,7 @@ sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
|
|
|
f07e16 |
struct se_device *dev = se_cmd->se_dev;
|
|
|
f07e16 |
struct xcopy_op *xop;
|
|
|
f07e16 |
unsigned int sa;
|
|
|
f07e16 |
+ struct percpu_ref **remote_lun_ref;
|
|
|
f07e16 |
|
|
|
f07e16 |
if (!dev->dev_attrib.emulate_3pc) {
|
|
|
f07e16 |
pr_err("EXTENDED_COPY operation explicitly disabled\n");
|
|
|
f07e16 |
@@ -1006,6 +1045,12 @@ sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
|
|
|
f07e16 |
xop = kzalloc(sizeof(struct xcopy_op), GFP_KERNEL);
|
|
|
f07e16 |
if (!xop)
|
|
|
f07e16 |
goto err;
|
|
|
f07e16 |
+
|
|
|
f07e16 |
+ remote_lun_ref = klp_shadow_alloc(xop, KLP_SHADOW_REMOTE_LUN_REF,
|
|
|
f07e16 |
+ sizeof(struct percpu_ref*), GFP_KERNEL, NULL, NULL);
|
|
|
f07e16 |
+ if (!remote_lun_ref)
|
|
|
f07e16 |
+ goto xop_free;
|
|
|
f07e16 |
+
|
|
|
f07e16 |
xop->xop_se_cmd = se_cmd;
|
|
|
f07e16 |
INIT_WORK(&xop->xop_work, target_xcopy_do_work);
|
|
|
f07e16 |
if (WARN_ON_ONCE(!queue_work(xcopy_wq, &xop->xop_work)))
|
|
|
f07e16 |
@@ -1013,6 +1058,8 @@ sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
|
|
|
f07e16 |
return TCM_NO_SENSE;
|
|
|
f07e16 |
|
|
|
f07e16 |
free:
|
|
|
f07e16 |
+ klp_shadow_free(xop, KLP_SHADOW_REMOTE_LUN_REF, NULL);
|
|
|
f07e16 |
+xop_free:
|
|
|
f07e16 |
kfree(xop);
|
|
|
f07e16 |
|
|
|
f07e16 |
err:
|
|
|
f07e16 |
--
|
|
|
f07e16 |
2.26.2
|
|
|
f07e16 |
|