int bestcnt = 0;
int devcnt;
unsigned int okcnt, sparecnt, rebuilding_cnt, replcnt, journalcnt;
+ int journal_clean = 0;
int i;
int was_forced = 0;
int most_recent = 0;
) {
devices[j].uptodate = 1;
if (devices[j].i.disk.state & (1<<MD_DISK_JOURNAL))
- content->journal_clean = 1;
+ journal_clean = 1;
if (i < content->array.raid_disks * 2) {
if (devices[j].i.recovery_start == MaxSector ||
(content->reshape_active &&
#ifndef MDASSEMBLE
sysfs_init(content, mdfd, NULL);
#endif
+ /* after reload context, store journal_clean in context */
+ content->journal_clean = journal_clean;
for (i=0; i<bestcnt; i++) {
int j = best[i];
unsigned int desired_state;
for (d = 0; d < max_disks * 2; d++) {
disks[d].state = (1<<MD_DISK_REMOVED);
disks[d].major = disks[d].minor = 0;
- disks[d].number = disks[d].raid_disk = d;
+ disks[d].number = -1;
+ disks[d].raid_disk = d/2;
}
next = array.raid_disks*2;
else if (disk.raid_disk < 0)
printf(" %5d %5d %5d - ",
disk.number, disk.major, disk.minor);
+ else if (disk.number < 0)
+ printf(" - %5d %5d %5d ",
+ disk.major, disk.minor, disk.raid_disk);
else
printf(" %5d %5d %5d %5d ",
disk.number, disk.major, disk.minor, disk.raid_disk);
if (strcmp(s->bitmap_file, "none")==0) {
array.state &= ~(1<<MD_SB_BITMAP_PRESENT);
if (ioctl(fd, SET_ARRAY_INFO, &array)!= 0) {
- pr_err("failed to remove internal bitmap.\n");
+ if (array.state & (1<<MD_SB_CLUSTERED))
+ pr_err("failed to remove clustered bitmap.\n");
+ else
+ pr_err("failed to remove internal bitmap.\n");
return 1;
}
return 0;
}
- pr_err("%s bitmap already present on %s\n", s->bitmap_file, devname);
+ pr_err("bitmap already present on %s\n", devname);
return 1;
}
* sometimes they aren't... So allow considerable flexability in matching, and allow
* this test to be overridden by an environment variable.
*/
- if (info->array.utime > (int)__le64_to_cpu(bsb.mtime) + 2*60*60 ||
- info->array.utime < (int)__le64_to_cpu(bsb.mtime) - 10*60) {
+ if(time_after(info->array.utime, (unsigned int)__le64_to_cpu(bsb.mtime) + 2*60*60) ||
+ time_before(info->array.utime, (unsigned int)__le64_to_cpu(bsb.mtime) - 10*60)) {
if (check_env("MDADM_GROW_ALLOW_OLD")) {
pr_err("accepting backup with timestamp %lu for array with timestamp %lu\n",
(unsigned long)__le64_to_cpu(bsb.mtime),
/* add disk needs to know about containers */
if (st->ss->external)
sra->array.level = LEVEL_CONTAINER;
+
+ if (info.array.state & (1 << MD_SB_CLUSTERED))
+ info.disk.state |= (1 << MD_DISK_CLUSTER_ADD);
+
err = add_disk(mdfd, st, sra, &info);
if (err < 0 && errno == EBUSY) {
/* could be another device present with the same
mdopen.o super0.o super1.o super-ddf.o super-intel.o bitmap.o \
super-mbr.o super-gpt.o \
restripe.o sysfs.o sha1.o mapfile.o crc32.o sg_io.o msg.o xmalloc.o \
- platform-intel.o probe_roms.o
+ platform-intel.o probe_roms.o crc32c.o
CHECK_OBJS = restripe.o sysfs.o maps.o lib.o xmalloc.o dlink.o
}
/* Make sure device is large enough */
- if (tst->sb &&
+ if (dv->disposition != 'j' && /* skip size check for Journal */
+ tst->sb &&
tst->ss->avail_size(tst, ldsize/512, INVALID_SECTORS) <
array_size) {
if (dv->disposition == 'M')
else
disc.number = raid_slot;
disc.state = 0;
+
+ /* only add journal to array that supports journaling */
+ if (dv->disposition == 'j') {
+ struct mdinfo mdi;
+ struct mdinfo *mdp;
+
+ mdp = sysfs_read(fd, NULL, GET_ARRAY_STATE);
+
+ if (strncmp(mdp->sysfs_array_state, "readonly", 8) != 0) {
+ pr_err("%s is not readonly, cannot add journal.\n", devname);
+ return -1;
+ }
+
+ tst->ss->getinfo_super(tst, &mdi, NULL);
+ if (mdi.journal_device_required == 0) {
+ pr_err("%s does not support journal device.\n", devname);
+ return -1;
+ }
+ disc.raid_disk = array->raid_disks;
+ }
+
if (array->not_persistent==0) {
int dfd;
+ if (dv->disposition == 'j')
+ disc.state |= (1 << MD_DISK_JOURNAL) | (1 << MD_DISK_SYNC);
if (dv->writemostly == 1)
disc.state |= 1 << MD_DISK_WRITEMOSTLY;
dfd = dev_open(dv->devname, O_RDWR | O_EXCL|O_DIRECT);
} else {
tst->ss->free_super(tst);
if (ioctl(fd, ADD_NEW_DISK, &disc)) {
- pr_err("add new device failed for %s as %d: %s\n",
- dv->devname, j, strerror(errno));
+ if (dv->disposition == 'j')
+ pr_err("Failed to hot add %s as journal, "
+ "please try restart %s.\n", dv->devname, devname);
+ else
+ pr_err("add new device failed for %s as %d: %s\n",
+ dv->devname, j, strerror(errno));
return -1;
}
+ if (dv->disposition == 'j') {
+ pr_err("Journal added successfully, making %s read-write\n", devname);
+ if (Manage_ro(devname, fd, -1))
+ pr_err("Failed to make %s read-write\n", devname);
+ }
+
}
if (verbose >= 0)
pr_err("added %s\n", dv->devname);
* try HOT_ADD_DISK
* If that fails EINVAL, try ADD_NEW_DISK
* 'S' - add the device as a spare - don't try re-add
+ * 'j' - add the device as a journal device
* 'A' - re-add the device
* 'r' - remove the device: HOT_REMOVE_DISK
* device can be 'faulty' or 'detached' in which case all
goto abort;
case 'a':
case 'S': /* --add-spare */
+ case 'j': /* --add-journal */
case 'A':
case 'M': /* --re-add missing */
case 'F': /* --re-add faulty */
/* Management */
{"add", 0, 0, Add},
{"add-spare", 0, 0, AddSpare},
+ {"add-journal", 0, 0, AddJournal},
{"remove", 0, 0, Remove},
{"fail", 0, 0, Fail},
{"set-faulty",0, 0, Fail},
pr_err("No bitmap possible with %s metadata\n",
st->ss->name);
return -1;
- } else
- st->ss->locate_bitmap(st, fd);
+ } else {
+ if (st->ss->locate_bitmap(st, fd)) {
+ pr_err("%s doesn't have bitmap\n", filename);
+ fd = -1;
+ }
+ }
*stp = st;
} else {
--- /dev/null
+/*
+ * Oct 28, 2015 Song Liu simplified the code and port it to mdadm
+ *
+ * Aug 8, 2011 Bob Pearson with help from Joakim Tjernlund and George Spelvin
+ * cleaned up code to current version of sparse and added the slicing-by-8
+ * algorithm to the closely similar existing slicing-by-4 algorithm.
+ *
+ * Oct 15, 2000 Matt Domsch <Matt_Domsch@dell.com>
+ * Nicer crc32 functions/docs submitted by linux@horizon.com. Thanks!
+ * Code was from the public domain, copyright abandoned. Code was
+ * subsequently included in the kernel, thus was re-licensed under the
+ * GNU GPL v2.
+ *
+ * Oct 12, 2000 Matt Domsch <Matt_Domsch@dell.com>
+ * Same crc32 function was used in 5 other places in the kernel.
+ * I made one version, and deleted the others.
+ * There are various incantations of crc32(). Some use a seed of 0 or ~0.
+ * Some xor at the end with ~0. The generic crc32() function takes
+ * seed as an argument, and doesn't xor at the end. Then individual
+ * users can do whatever they need.
+ * drivers/net/smc9194.c uses seed ~0, doesn't xor with ~0.
+ * fs/jffs2 uses seed 0, doesn't xor with ~0.
+ * fs/partitions/efi.c uses seed ~0, xor's with ~0.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <sys/types.h>
+#include <asm/types.h>
+#include <stdlib.h>
+
+/*
+ * There are multiple 16-bit CRC polynomials in common use, but this is
+ * *the* standard CRC-32 polynomial, first popularized by Ethernet.
+ * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
+ */
+#define CRCPOLY_LE 0xedb88320
+#define CRCPOLY_BE 0x04c11db7
+
+/*
+ * This is the CRC32c polynomial, as outlined by Castagnoli.
+ * x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+x^11+x^10+x^9+
+ * x^8+x^6+x^0
+ */
+#define CRC32C_POLY_LE 0x82F63B78
+
+/**
+ * crc32_le_generic() - Calculate bitwise little-endian Ethernet AUTODIN II
+ * CRC32/CRC32C
+ * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for other
+ * uses, or the previous crc32/crc32c value if computing incrementally.
+ * @p: pointer to buffer over which CRC32/CRC32C is run
+ * @len: length of buffer @p
+ * @polynomial: CRC32/CRC32c LE polynomial
+ */
+static inline __u32 crc32_le_generic(__u32 crc, unsigned char const *p,
+ size_t len, __u32 polynomial)
+{
+ int i;
+ while (len--) {
+ crc ^= *p++;
+ for (i = 0; i < 8; i++)
+ crc = (crc >> 1) ^ ((crc & 1) ? polynomial : 0);
+ }
+ return crc;
+}
+
+__u32 crc32_le(__u32 crc, unsigned char const *p, size_t len)
+{
+ return crc32_le_generic(crc, p, len, CRCPOLY_LE);
+}
+
+__u32 crc32c_le(__u32 crc, unsigned char const *p, size_t len)
+{
+ return crc32_le_generic(crc, p, len, CRC32C_POLY_LE);
+}
+
+/**
+ * crc32_be_generic() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
+ * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for
+ * other uses, or the previous crc32 value if computing incrementally.
+ * @p: pointer to buffer over which CRC32 is run
+ * @len: length of buffer @p
+ * @polynomial: CRC32 BE polynomial
+ */
+static inline __u32 crc32_be_generic(__u32 crc, unsigned char const *p,
+ size_t len, __u32 polynomial)
+{
+ int i;
+ while (len--) {
+ crc ^= *p++ << 24;
+ for (i = 0; i < 8; i++)
+ crc =
+ (crc << 1) ^ ((crc & 0x80000000) ? polynomial :
+ 0);
+ }
+ return crc;
+}
+
+__u32 crc32_be(__u32 crc, unsigned char const *p, size_t len)
+{
+ return crc32_be_generic(crc, p, len, CRCPOLY_BE);
+}
int major_version;
int minor_version;
int patch_version;
- int ctime;
+ unsigned int ctime;
int level;
int size;
int nr_disks;
/*
* Generic state information
*/
- int utime; /* 0 Superblock update time */
+ unsigned int utime; /* 0 Superblock update time */
int state; /* 1 State bits (clean, ...) */
int active_disks; /* 2 Number of currently active disks */
int working_disks; /* 3 Number of working disks */
case 'a':
case Add:
case AddSpare:
+ case AddJournal:
case 'r':
case Remove:
case Replace:
case O(MANAGE,AddSpare): /* add drive - never re-add */
devmode = 'S';
continue;
+ case O(MANAGE,AddJournal): /* add journal */
+ if (s.journaldisks && (s.level < 4 || s.level > 6)) {
+ pr_err("--add-journal is only supported for RAID level 4/5/6.\n");
+ exit(2);
+ }
+ devmode = 'j';
+ continue;
case O(MANAGE,ReAdd):
devmode = 'A';
continue;
#endif
#endif /* __KLIBC__ */
+/*
+ * Check at compile time that something is of a particular type.
+ * Always evaluates to 1 so you may use it easily in comparisons.
+*/
+
+#define typecheck(type,x) \
+({ type __dummy; \
+ typeof(x) __dummy2; \
+ (void)(&__dummy == &__dummy2); \
+ 1; \
+})
+
+/*
+ * These inlines deal with timer wrapping correctly.
+ *
+ * time_after(a,b) returns true if the time a is after time b.
+*/
+
+#define time_after(a,b) \
+ (typecheck(unsigned int, a) && \
+ typecheck(unsigned int, b) && \
+ ((int)((b) - (a)) < 0))
+
+#define time_before(a,b) time_after(b,a)
+
/*
* min()/max()/clamp() macros that also do
* strict type-checking.. See the
#define DS_REMOVE 1024
#define DS_UNBLOCK 2048
int prev_state, curr_state, next_state;
+
+ /* info read from sysfs */
+ char sysfs_array_state[20];
};
struct createinfo {
ManageOpt,
Add,
AddSpare,
+ AddJournal,
Remove,
Fail,
Replace,
GET_SIZE = (1 << 22),
GET_STATE = (1 << 23),
GET_ERROR = (1 << 24),
+ GET_ARRAY_STATE = (1 << 25),
};
/* If fd >= 0, get the array it is open on,
/* Seek 'fd' to start of write-intent-bitmap. Must be an
* md-native format bitmap
*/
- void (*locate_bitmap)(struct supertype *st, int fd);
+ int (*locate_bitmap)(struct supertype *st, int fd);
/* if add_internal_bitmap succeeded for existing array, this
* writes it out.
*/
};
extern int get_cluster_name(char **name);
-extern int is_clustered(struct supertype *st);
-extern int cluster_get_dlmlock(struct supertype *st, int *lockid);
-extern int cluster_release_dlmlock(struct supertype *st, int lockid);
+extern int dlm_funs_ready(void);
+extern int cluster_get_dlmlock(int *lockid);
+extern int cluster_release_dlmlock(int lockid);
extern void set_dlm_hooks(void);
#define _ROUND_UP(val, base) (((val) + (base) - 1) & ~(base - 1))
return 1;
}
-static void locate_bitmap0(struct supertype *st, int fd)
+static int locate_bitmap0(struct supertype *st, int fd)
{
unsigned long long dsize;
unsigned long long offset;
if (!get_dev_size(fd, NULL, &dsize))
- return;
+ return -1;
if (dsize < MD_RESERVED_SECTORS*512)
- return;
+ return -1;
offset = MD_NEW_SIZE_SECTORS(dsize>>9);
offset += MD_SB_BYTES;
lseek64(fd, offset, 0);
+ return 0;
}
static int write_bitmap0(struct supertype *st, int fd, enum bitmap_update update)
strncmp(sb->set_name, homehost, l) == 0)
printf(" (local to host %s)", homehost);
printf("\n");
- if (bms->nodes > 0)
+ if (bms->nodes > 0 && (__le32_to_cpu(sb->feature_map) & MD_FEATURE_BITMAP_OFFSET))
printf(" Cluster Name : %-64s\n", bms->cluster_name);
atime = __le64_to_cpu(sb->ctime) & 0xFFFFFFFFFFULL;
printf(" Creation Time : %.24s\n", ctime(&atime));
sb->set_name[l] == ':' &&
strncmp(sb->set_name, homehost, l) == 0)
printf(" (local to host %s)", homehost);
- if (bms->nodes > 0)
+ if (bms->nodes > 0 && (__le32_to_cpu(sb->feature_map) & MD_FEATURE_BITMAP_OFFSET))
printf("\n Cluster Name : %-64s", bms->cluster_name);
printf("\n UUID : ");
for (i=0; i<16; i++) {
int rv = 0;
int lockid;
struct mdp_superblock_1 *sb = st->sb;
+ bitmap_super_t *bms = (bitmap_super_t*)(((char*)sb) + MAX_SB_SIZE);
- if (is_clustered(st)) {
- rv = cluster_get_dlmlock(st, &lockid);
+ if (bms->version == BITMAP_MAJOR_CLUSTERED && dlm_funs_ready()) {
+ rv = cluster_get_dlmlock(&lockid);
if (rv) {
pr_err("Cannot get dlmlock in %s return %d\n", __func__, rv);
- cluster_release_dlmlock(st, lockid);
+ cluster_release_dlmlock(lockid);
return rv;
}
}
rv = -1;
sb->sb_csum = calc_sb_1_csum(sb);
- if (is_clustered(st))
- cluster_release_dlmlock(st, lockid);
+ if (bms->version == BITMAP_MAJOR_CLUSTERED && dlm_funs_ready())
+ cluster_release_dlmlock(lockid);
return rv;
}
struct mdp_superblock_1 *sb = st->sb;
__u16 *rp = sb->dev_roles + dk->number;
struct devinfo *di, **dip;
+ bitmap_super_t *bms = (bitmap_super_t*)(((char*)sb) + MAX_SB_SIZE);
int rv, lockid;
- if (is_clustered(st)) {
- rv = cluster_get_dlmlock(st, &lockid);
+ if (bms->version == BITMAP_MAJOR_CLUSTERED && dlm_funs_ready()) {
+ rv = cluster_get_dlmlock(&lockid);
if (rv) {
pr_err("Cannot get dlmlock in %s return %d\n", __func__, rv);
- cluster_release_dlmlock(st, lockid);
+ cluster_release_dlmlock(lockid);
return rv;
}
}
di->next = NULL;
*dip = di;
- if (is_clustered(st))
- cluster_release_dlmlock(st, lockid);
+ if (bms->version == BITMAP_MAJOR_CLUSTERED && dlm_funs_ready())
+ cluster_release_dlmlock(lockid);
return 0;
}
#endif
-static void locate_bitmap1(struct supertype *st, int fd);
+static int locate_bitmap1(struct supertype *st, int fd);
static int store_super1(struct supertype *st, int fd)
{
struct align_fd afd;
int sbsize;
unsigned long long dsize;
+ bitmap_super_t *bms = (bitmap_super_t*)(((char*)sb) + MAX_SB_SIZE);
int rv, lockid;
- if (is_clustered(st)) {
- rv = cluster_get_dlmlock(st, &lockid);
+ if (bms->version == BITMAP_MAJOR_CLUSTERED && dlm_funs_ready()) {
+ rv = cluster_get_dlmlock(&lockid);
if (rv) {
pr_err("Cannot get dlmlock in %s return %d\n", __func__, rv);
- cluster_release_dlmlock(st, lockid);
+ cluster_release_dlmlock(lockid);
return rv;
}
}
}
}
fsync(fd);
- if (is_clustered(st))
- cluster_release_dlmlock(st, lockid);
+ if (bms->version == BITMAP_MAJOR_CLUSTERED && dlm_funs_ready())
+ cluster_release_dlmlock(lockid);
return 0;
}
static void free_super1(struct supertype *st);
#define META_BLOCK_SIZE 4096
-unsigned long crc32(
- unsigned long crc,
- const unsigned char *buf,
- unsigned len);
+__u32 crc32c_le(__u32 crc, unsigned char const *p, size_t len);
static int write_empty_r5l_meta_block(struct supertype *st, int fd)
{
mb->seq = __cpu_to_le64(random32());
mb->position = __cpu_to_le64(0);
- crc = crc32(0xffffffff, sb->set_uuid, sizeof(sb->set_uuid));
- crc = crc32(crc, (void *)mb, META_BLOCK_SIZE);
- mb->checksum = __cpu_to_le32(crc);
+ crc = crc32c_le(0xffffffff, sb->set_uuid, sizeof(sb->set_uuid));
+ crc = crc32c_le(crc, (void *)mb, META_BLOCK_SIZE);
+ mb->checksum = crc;
if (lseek64(fd, (sb->data_offset) * 512, 0) < 0LL) {
pr_err("cannot seek to offset of the meta block\n");
if (rfd >= 0)
close(rfd);
- sb->events = 0;
+ if (!(di->disk.state & (1<<MD_DISK_JOURNAL)))
+ sb->events = 0;
refst = dup_super(st);
if (load_super1(refst, di->fd, NULL)==0) {
return 1;
}
-static void locate_bitmap1(struct supertype *st, int fd)
+static int locate_bitmap1(struct supertype *st, int fd)
{
unsigned long long offset;
struct mdp_superblock_1 *sb;
int mustfree = 0;
+ int ret;
if (!st->sb) {
if (st->ss->load_super(st, fd, NULL))
- return; /* no error I hope... */
+ return -1; /* no error I hope... */
mustfree = 1;
}
sb = st->sb;
+ if ((__le32_to_cpu(sb->feature_map) & MD_FEATURE_BITMAP_OFFSET))
+ ret = 0;
+ else
+ ret = -1;
offset = __le64_to_cpu(sb->super_offset);
offset += (int32_t) __le32_to_cpu(sb->bitmap_offset);
if (mustfree)
free(sb);
lseek64(fd, offset<<9, 0);
+ return ret;
}
static int write_bitmap1(struct supertype *st, int fd, enum bitmap_update update)
static void free_super1(struct supertype *st)
{
- int rv, lockid;
- if (is_clustered(st)) {
- rv = cluster_get_dlmlock(st, &lockid);
- if (rv) {
- pr_err("Cannot get dlmlock in %s return %d\n", __func__, rv);
- cluster_release_dlmlock(st, lockid);
- return;
- }
- }
if (st->sb)
free(st->sb);
free(di);
}
st->sb = NULL;
- if (is_clustered(st))
- cluster_release_dlmlock(st, lockid);
}
#ifndef MDASSEMBLE
goto abort;
}
+ if (options & GET_ARRAY_STATE) {
+ strcpy(base, "array_state");
+ if (load_sys(fname, sra->sysfs_array_state))
+ goto abort;
+ } else
+ sra->sysfs_array_state[0] = 0;
+
if (! (options & GET_DEVS))
return sra;
struct dlm_lksb lksb;
};
-int is_clustered(struct supertype *st)
+int dlm_funs_ready(void)
{
- /* is it a cluster md or not */
- if (is_dlm_hooks_ready && st->cluster_name)
- return 1;
- else
- return 0;
+ return is_dlm_hooks_ready ? 1 : 0;
}
/* Using poll(2) to wait for and dispatch ASTs */
ast_called = 1;
}
+static char *cluster_name = NULL;
/* Create the lockspace, take bitmapXXX locks on all the bitmaps. */
-int cluster_get_dlmlock(struct supertype *st, int *lockid)
+int cluster_get_dlmlock(int *lockid)
{
int ret = -1;
char str[64];
int flags = LKF_NOQUEUE;
+ ret = get_cluster_name(&cluster_name);
+ if (ret) {
+ pr_err("The md can't get cluster name\n");
+ return -1;
+ }
+
dlm_lock_res = xmalloc(sizeof(struct dlm_lock_resource));
- dlm_lock_res->ls = dlm_hooks->create_lockspace(st->cluster_name, O_RDWR);
+ dlm_lock_res->ls = dlm_hooks->create_lockspace(cluster_name, O_RDWR);
if (!dlm_lock_res->ls) {
- pr_err("%s failed to create lockspace\n", st->cluster_name);
+ pr_err("%s failed to create lockspace\n", cluster_name);
goto out;
}
if (flags & LKF_CONVERT)
dlm_lock_res->lksb.sb_lkid = *lockid;
- snprintf(str, 64, "bitmap%04d", st->nodes);
+ snprintf(str, 64, "bitmap%s", cluster_name);
/* if flags with LKF_CONVERT causes below return ENOENT which means
* "No such file or directory" */
ret = dlm_hooks->ls_lock(dlm_lock_res->ls, LKM_PWMODE, &dlm_lock_res->lksb,
return ret;
}
-int cluster_release_dlmlock(struct supertype *st, int lockid)
+int cluster_release_dlmlock(int lockid)
{
int ret = -1;
+ if (!cluster_name)
+ return -1;
+
/* if flags with LKF_CONVERT causes below return EINVAL which means
* "Invalid argument" */
ret = dlm_hooks->ls_unlock(dlm_lock_res->ls, lockid, 0,
goto out;
}
- ret = dlm_hooks->release_lockspace(st->cluster_name, dlm_lock_res->ls, 1);
+ ret = dlm_hooks->release_lockspace(cluster_name, dlm_lock_res->ls, 1);
if (ret) {
pr_err("error %d happened when release lockspace\n", errno);
/* XXX make sure the lockspace is released eventually */