Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
6208e77e
Commit
6208e77e
authored
Feb 03, 2008
by
David Woodhouse
Browse files
Options
Browse Files
Download
Plain Diff
Merge
git://git.infradead.org/~dedekind/ubi-2.6
parents
5eb91034
6dc4a871
Changes
13
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
441 additions
and
188 deletions
+441
-188
drivers/mtd/ubi/build.c
drivers/mtd/ubi/build.c
+68
-35
drivers/mtd/ubi/cdev.c
drivers/mtd/ubi/cdev.c
+64
-18
drivers/mtd/ubi/eba.c
drivers/mtd/ubi/eba.c
+19
-21
drivers/mtd/ubi/kapi.c
drivers/mtd/ubi/kapi.c
+4
-6
drivers/mtd/ubi/scan.c
drivers/mtd/ubi/scan.c
+7
-3
drivers/mtd/ubi/ubi.h
drivers/mtd/ubi/ubi.h
+39
-16
drivers/mtd/ubi/upd.c
drivers/mtd/ubi/upd.c
+131
-45
drivers/mtd/ubi/vmt.c
drivers/mtd/ubi/vmt.c
+1
-13
drivers/mtd/ubi/vtbl.c
drivers/mtd/ubi/vtbl.c
+17
-6
drivers/mtd/ubi/wl.c
drivers/mtd/ubi/wl.c
+0
-1
include/linux/mtd/ubi.h
include/linux/mtd/ubi.h
+0
-17
include/mtd/ubi-header.h
include/mtd/ubi-header.h
+44
-3
include/mtd/ubi-user.h
include/mtd/ubi-user.h
+47
-4
No files found.
drivers/mtd/ubi/build.c
View file @
6208e77e
...
...
@@ -66,9 +66,6 @@ static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES];
/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */
struct
class
*
ubi_class
;
/* Slab cache for lock-tree entries */
struct
kmem_cache
*
ubi_ltree_slab
;
/* Slab cache for wear-leveling entries */
struct
kmem_cache
*
ubi_wl_entry_slab
;
...
...
@@ -369,9 +366,6 @@ static int uif_init(struct ubi_device *ubi)
int
i
,
err
;
dev_t
dev
;
mutex_init
(
&
ubi
->
volumes_mutex
);
spin_lock_init
(
&
ubi
->
volumes_lock
);
sprintf
(
ubi
->
ubi_name
,
UBI_NAME_STR
"%d"
,
ubi
->
ubi_num
);
/*
...
...
@@ -568,7 +562,7 @@ static int io_init(struct ubi_device *ubi)
}
/* Similar for the data offset */
ubi
->
leb_start
=
ubi
->
vid_hdr_offset
+
ubi
->
vid_hdr_alsize
;
ubi
->
leb_start
=
ubi
->
vid_hdr_offset
+
UBI_EC_HDR_SIZE
;
ubi
->
leb_start
=
ALIGN
(
ubi
->
leb_start
,
ubi
->
min_io_size
);
dbg_msg
(
"vid_hdr_offset %d"
,
ubi
->
vid_hdr_offset
);
...
...
@@ -626,6 +620,58 @@ static int io_init(struct ubi_device *ubi)
return
0
;
}
/**
* autoresize - re-size the volume which has the "auto-resize" flag set.
* @ubi: UBI device description object
* @vol_id: ID of the volume to re-size
*
* This function re-sizes the volume marked by the @UBI_VTBL_AUTORESIZE_FLG in
* the volume table to the largest possible size. See comments in ubi-header.h
* for more description of the flag. Returns zero in case of success and a
* negative error code in case of failure.
*/
static
int
autoresize
(
struct
ubi_device
*
ubi
,
int
vol_id
)
{
struct
ubi_volume_desc
desc
;
struct
ubi_volume
*
vol
=
ubi
->
volumes
[
vol_id
];
int
err
,
old_reserved_pebs
=
vol
->
reserved_pebs
;
/*
* Clear the auto-resize flag in the volume in-memory copy of the
* volume table, and 'ubi_resize_volume()' will propogate this change
* to the flash.
*/
ubi
->
vtbl
[
vol_id
].
flags
&=
~
UBI_VTBL_AUTORESIZE_FLG
;
if
(
ubi
->
avail_pebs
==
0
)
{
struct
ubi_vtbl_record
vtbl_rec
;
/*
* No avalilable PEBs to re-size the volume, clear the flag on
* flash and exit.
*/
memcpy
(
&
vtbl_rec
,
&
ubi
->
vtbl
[
vol_id
],
sizeof
(
struct
ubi_vtbl_record
));
err
=
ubi_change_vtbl_record
(
ubi
,
vol_id
,
&
vtbl_rec
);
if
(
err
)
ubi_err
(
"cannot clean auto-resize flag for volume %d"
,
vol_id
);
}
else
{
desc
.
vol
=
vol
;
err
=
ubi_resize_volume
(
&
desc
,
old_reserved_pebs
+
ubi
->
avail_pebs
);
if
(
err
)
ubi_err
(
"cannot auto-resize volume %d"
,
vol_id
);
}
if
(
err
)
return
err
;
ubi_msg
(
"volume %d (
\"
%s
\"
) re-sized from %d to %d LEBs"
,
vol_id
,
vol
->
name
,
old_reserved_pebs
,
vol
->
reserved_pebs
);
return
0
;
}
/**
* ubi_attach_mtd_dev - attach an MTD device.
* @mtd_dev: MTD device description object
...
...
@@ -702,6 +748,12 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
ubi
->
mtd
=
mtd
;
ubi
->
ubi_num
=
ubi_num
;
ubi
->
vid_hdr_offset
=
vid_hdr_offset
;
ubi
->
autoresize_vol_id
=
-
1
;
mutex_init
(
&
ubi
->
buf_mutex
);
mutex_init
(
&
ubi
->
ckvol_mutex
);
mutex_init
(
&
ubi
->
volumes_mutex
);
spin_lock_init
(
&
ubi
->
volumes_lock
);
dbg_msg
(
"attaching mtd%d to ubi%d: VID header offset %d"
,
mtd
->
index
,
ubi_num
,
vid_hdr_offset
);
...
...
@@ -710,8 +762,6 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
if
(
err
)
goto
out_free
;
mutex_init
(
&
ubi
->
buf_mutex
);
mutex_init
(
&
ubi
->
ckvol_mutex
);
ubi
->
peb_buf1
=
vmalloc
(
ubi
->
peb_size
);
if
(
!
ubi
->
peb_buf1
)
goto
out_free
;
...
...
@@ -733,6 +783,12 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
goto
out_free
;
}
if
(
ubi
->
autoresize_vol_id
!=
-
1
)
{
err
=
autoresize
(
ubi
,
ubi
->
autoresize_vol_id
);
if
(
err
)
goto
out_detach
;
}
err
=
uif_init
(
ubi
);
if
(
err
)
goto
out_detach
;
...
...
@@ -857,20 +913,6 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
return
0
;
}
/**
* ltree_entry_ctor - lock tree entries slab cache constructor.
* @obj: the lock-tree entry to construct
* @cache: the lock tree entry slab cache
* @flags: constructor flags
*/
static
void
ltree_entry_ctor
(
struct
kmem_cache
*
cache
,
void
*
obj
)
{
struct
ubi_ltree_entry
*
le
=
obj
;
le
->
users
=
0
;
init_rwsem
(
&
le
->
mutex
);
}
/**
* find_mtd_device - open an MTD device by its name or number.
* @mtd_dev: name or number of the device
...
...
@@ -933,17 +975,11 @@ static int __init ubi_init(void)
goto
out_version
;
}
ubi_ltree_slab
=
kmem_cache_create
(
"ubi_ltree_slab"
,
sizeof
(
struct
ubi_ltree_entry
),
0
,
0
,
&
ltree_entry_ctor
);
if
(
!
ubi_ltree_slab
)
goto
out_dev_unreg
;
ubi_wl_entry_slab
=
kmem_cache_create
(
"ubi_wl_entry_slab"
,
sizeof
(
struct
ubi_wl_entry
),
0
,
0
,
NULL
);
if
(
!
ubi_wl_entry_slab
)
goto
out_
ltree
;
goto
out_
dev_unreg
;
/* Attach MTD devices */
for
(
i
=
0
;
i
<
mtd_devs
;
i
++
)
{
...
...
@@ -980,8 +1016,6 @@ static int __init ubi_init(void)
mutex_unlock
(
&
ubi_devices_mutex
);
}
kmem_cache_destroy
(
ubi_wl_entry_slab
);
out_ltree:
kmem_cache_destroy
(
ubi_ltree_slab
);
out_dev_unreg:
misc_deregister
(
&
ubi_ctrl_cdev
);
out_version:
...
...
@@ -1005,7 +1039,6 @@ static void __exit ubi_exit(void)
mutex_unlock
(
&
ubi_devices_mutex
);
}
kmem_cache_destroy
(
ubi_wl_entry_slab
);
kmem_cache_destroy
(
ubi_ltree_slab
);
misc_deregister
(
&
ubi_ctrl_cdev
);
class_remove_file
(
ubi_class
,
&
ubi_version
);
class_destroy
(
ubi_class
);
...
...
@@ -1066,7 +1099,7 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp)
struct
mtd_dev_param
*
p
;
char
buf
[
MTD_PARAM_LEN_MAX
];
char
*
pbuf
=
&
buf
[
0
];
char
*
tokens
[
3
]
=
{
NULL
,
NULL
,
NULL
};
char
*
tokens
[
2
]
=
{
NULL
,
NULL
};
if
(
!
val
)
return
-
EINVAL
;
...
...
@@ -1096,7 +1129,7 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp)
if
(
buf
[
len
-
1
]
==
'\n'
)
buf
[
len
-
1
]
=
'\0'
;
for
(
i
=
0
;
i
<
3
;
i
++
)
for
(
i
=
0
;
i
<
2
;
i
++
)
tokens
[
i
]
=
strsep
(
&
pbuf
,
","
);
if
(
pbuf
)
{
...
...
drivers/mtd/ubi/cdev.c
View file @
6208e77e
...
...
@@ -132,8 +132,15 @@ static int vol_cdev_release(struct inode *inode, struct file *file)
if
(
vol
->
updating
)
{
ubi_warn
(
"update of volume %d not finished, volume is damaged"
,
vol
->
vol_id
);
ubi_assert
(
!
vol
->
changing_leb
);
vol
->
updating
=
0
;
vfree
(
vol
->
upd_buf
);
}
else
if
(
vol
->
changing_leb
)
{
dbg_msg
(
"only %lld of %lld bytes received for atomic LEB change"
" for volume %d:%d, cancel"
,
vol
->
upd_received
,
vol
->
upd_bytes
,
vol
->
ubi
->
ubi_num
,
vol
->
vol_id
);
vol
->
changing_leb
=
0
;
vfree
(
vol
->
upd_buf
);
}
ubi_close_volume
(
desc
);
...
...
@@ -184,13 +191,13 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count,
struct
ubi_volume_desc
*
desc
=
file
->
private_data
;
struct
ubi_volume
*
vol
=
desc
->
vol
;
struct
ubi_device
*
ubi
=
vol
->
ubi
;
int
err
,
lnum
,
off
,
len
,
vol_id
=
desc
->
vol
->
vol_id
,
tbuf_size
;
int
err
,
lnum
,
off
,
len
,
tbuf_size
;
size_t
count_save
=
count
;
void
*
tbuf
;
uint64_t
tmp
;
dbg_msg
(
"read %zd bytes from offset %lld of volume %d"
,
count
,
*
offp
,
vol_id
);
count
,
*
offp
,
vol
->
vol
_id
);
if
(
vol
->
updating
)
{
dbg_err
(
"updating"
);
...
...
@@ -204,7 +211,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count,
return
0
;
if
(
vol
->
corrupted
)
dbg_msg
(
"read from corrupted volume %d"
,
vol_id
);
dbg_msg
(
"read from corrupted volume %d"
,
vol
->
vol
_id
);
if
(
*
offp
+
count
>
vol
->
used_bytes
)
count_save
=
count
=
vol
->
used_bytes
-
*
offp
;
...
...
@@ -274,7 +281,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
uint64_t
tmp
;
dbg_msg
(
"requested: write %zd bytes to offset %lld of volume %u"
,
count
,
*
offp
,
desc
->
vol
->
vol_id
);
count
,
*
offp
,
vol
->
vol_id
);
if
(
vol
->
vol_type
==
UBI_STATIC_VOLUME
)
return
-
EROFS
;
...
...
@@ -351,23 +358,32 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf,
struct
ubi_volume
*
vol
=
desc
->
vol
;
struct
ubi_device
*
ubi
=
vol
->
ubi
;
if
(
!
vol
->
updating
)
if
(
!
vol
->
updating
&&
!
vol
->
changing_leb
)
return
vol_cdev_direct_write
(
file
,
buf
,
count
,
offp
);
err
=
ubi_more_update_data
(
ubi
,
vol
->
vol_id
,
buf
,
count
);
if
(
vol
->
updating
)
err
=
ubi_more_update_data
(
ubi
,
vol
,
buf
,
count
);
else
err
=
ubi_more_leb_change_data
(
ubi
,
vol
,
buf
,
count
);
if
(
err
<
0
)
{
ubi_err
(
"cannot
write %zd bytes of update
data, error %d"
,
ubi_err
(
"cannot
accept more %zd bytes of
data, error %d"
,
count
,
err
);
return
err
;
}
if
(
err
)
{
/*
*
Update is finished, @err contains number of actually written
*
bytes now
.
*
The operation is finished, @err contains number of actually
*
written bytes
.
*/
count
=
err
;
if
(
vol
->
changing_leb
)
{
revoke_exclusive
(
desc
,
UBI_READWRITE
);
return
count
;
}
err
=
ubi_check_volume
(
ubi
,
vol
->
vol_id
);
if
(
err
<
0
)
return
err
;
...
...
@@ -382,7 +398,6 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf,
revoke_exclusive
(
desc
,
UBI_READWRITE
);
}
*
offp
+=
count
;
return
count
;
}
...
...
@@ -427,11 +442,46 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file,
if
(
err
<
0
)
break
;
err
=
ubi_start_update
(
ubi
,
vol
->
vol_id
,
bytes
);
err
=
ubi_start_update
(
ubi
,
vol
,
bytes
);
if
(
bytes
==
0
)
revoke_exclusive
(
desc
,
UBI_READWRITE
);
break
;
}
/* Atomic logical eraseblock change command */
case
UBI_IOCEBCH
:
{
struct
ubi_leb_change_req
req
;
err
=
copy_from_user
(
&
req
,
argp
,
sizeof
(
struct
ubi_leb_change_req
));
if
(
err
)
{
err
=
-
EFAULT
;
break
;
}
if
(
desc
->
mode
==
UBI_READONLY
||
vol
->
vol_type
==
UBI_STATIC_VOLUME
)
{
err
=
-
EROFS
;
break
;
}
/* Validate the request */
err
=
-
EINVAL
;
if
(
req
.
lnum
<
0
||
req
.
lnum
>=
vol
->
reserved_pebs
||
req
.
bytes
<
0
||
req
.
lnum
>=
vol
->
usable_leb_size
)
break
;
if
(
req
.
dtype
!=
UBI_LONGTERM
&&
req
.
dtype
!=
UBI_SHORTTERM
&&
req
.
dtype
!=
UBI_UNKNOWN
)
break
;
err
=
get_exclusive
(
desc
);
if
(
err
<
0
)
break
;
file
->
f_pos
=
0
;
err
=
ubi_start_leb_change
(
ubi
,
vol
,
&
req
);
if
(
req
.
bytes
==
0
)
revoke_exclusive
(
desc
,
UBI_READWRITE
);
break
;
}
...
...
@@ -447,7 +497,8 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file,
break
;
}
if
(
desc
->
mode
==
UBI_READONLY
)
{
if
(
desc
->
mode
==
UBI_READONLY
||
vol
->
vol_type
==
UBI_STATIC_VOLUME
)
{
err
=
-
EROFS
;
break
;
}
...
...
@@ -457,11 +508,6 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file,
break
;
}
if
(
vol
->
vol_type
!=
UBI_DYNAMIC_VOLUME
)
{
err
=
-
EROFS
;
break
;
}
dbg_msg
(
"erase LEB %d:%d"
,
vol
->
vol_id
,
lnum
);
err
=
ubi_eba_unmap_leb
(
ubi
,
vol
,
lnum
);
if
(
err
)
...
...
drivers/mtd/ubi/eba.c
View file @
6208e77e
...
...
@@ -78,7 +78,7 @@ static unsigned long long next_sqnum(struct ubi_device *ubi)
*/
static
int
ubi_get_compat
(
const
struct
ubi_device
*
ubi
,
int
vol_id
)
{
if
(
vol_id
==
UBI_LAYOUT_VOL_ID
)
if
(
vol_id
==
UBI_LAYOUT_VOL
UME
_ID
)
return
UBI_LAYOUT_VOLUME_COMPAT
;
return
0
;
}
...
...
@@ -137,10 +137,12 @@ static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi,
{
struct
ubi_ltree_entry
*
le
,
*
le1
,
*
le_free
;
le
=
km
em_cache_alloc
(
ubi_ltree_slab
,
GFP_NOFS
);
le
=
km
alloc
(
sizeof
(
struct
ubi_ltree_entry
)
,
GFP_NOFS
);
if
(
!
le
)
return
ERR_PTR
(
-
ENOMEM
);
le
->
users
=
0
;
init_rwsem
(
&
le
->
mutex
);
le
->
vol_id
=
vol_id
;
le
->
lnum
=
lnum
;
...
...
@@ -188,7 +190,7 @@ static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi,
spin_unlock
(
&
ubi
->
ltree_lock
);
if
(
le_free
)
k
mem_cache_free
(
ubi_ltree_slab
,
le_free
);
k
free
(
le_free
);
return
le
;
}
...
...
@@ -236,7 +238,7 @@ static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum)
up_read
(
&
le
->
mutex
);
if
(
free
)
k
mem_cache_free
(
ubi_ltree_slab
,
le
);
k
free
(
le
);
}
/**
...
...
@@ -292,7 +294,7 @@ static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum)
free
=
0
;
spin_unlock
(
&
ubi
->
ltree_lock
);
if
(
free
)
k
mem_cache_free
(
ubi_ltree_slab
,
le
);
k
free
(
le
);
return
1
;
}
...
...
@@ -321,7 +323,7 @@ static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum)
up_write
(
&
le
->
mutex
);
if
(
free
)
k
mem_cache_free
(
ubi_ltree_slab
,
le
);
k
free
(
le
);
}
/**
...
...
@@ -339,9 +341,6 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
{
int
err
,
pnum
,
vol_id
=
vol
->
vol_id
;
ubi_assert
(
ubi
->
ref_count
>
0
);
ubi_assert
(
vol
->
ref_count
>
0
);
if
(
ubi
->
ro_mode
)
return
-
EROFS
;
...
...
@@ -390,9 +389,6 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
struct
ubi_vid_hdr
*
vid_hdr
;
uint32_t
uninitialized_var
(
crc
);
ubi_assert
(
ubi
->
ref_count
>
0
);
ubi_assert
(
vol
->
ref_count
>
0
);
err
=
leb_read_lock
(
ubi
,
vol_id
,
lnum
);
if
(
err
)
return
err
;
...
...
@@ -616,9 +612,6 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
int
err
,
pnum
,
tries
=
0
,
vol_id
=
vol
->
vol_id
;
struct
ubi_vid_hdr
*
vid_hdr
;
ubi_assert
(
ubi
->
ref_count
>
0
);
ubi_assert
(
vol
->
ref_count
>
0
);
if
(
ubi
->
ro_mode
)
return
-
EROFS
;
...
...
@@ -752,9 +745,6 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
struct
ubi_vid_hdr
*
vid_hdr
;
uint32_t
crc
;
ubi_assert
(
ubi
->
ref_count
>
0
);
ubi_assert
(
vol
->
ref_count
>
0
);
if
(
ubi
->
ro_mode
)
return
-
EROFS
;
...
...
@@ -869,12 +859,20 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
struct
ubi_vid_hdr
*
vid_hdr
;
uint32_t
crc
;
ubi_assert
(
ubi
->
ref_count
>
0
);
ubi_assert
(
vol
->
ref_count
>
0
);
if
(
ubi
->
ro_mode
)
return
-
EROFS
;
if
(
len
==
0
)
{
/*
* Special case when data length is zero. In this case the LEB
* has to be unmapped and mapped somewhere else.
*/
err
=
ubi_eba_unmap_leb
(
ubi
,
vol
,
lnum
);
if
(
err
)
return
err
;
return
ubi_eba_write_leb
(
ubi
,
vol
,
lnum
,
NULL
,
0
,
0
,
dtype
);
}
vid_hdr
=
ubi_zalloc_vid_hdr
(
ubi
,
GFP_NOFS
);
if
(
!
vid_hdr
)
return
-
ENOMEM
;
...
...
drivers/mtd/ubi/kapi.c
View file @
6208e77e
...
...
@@ -480,9 +480,9 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum)
{
struct
ubi_volume
*
vol
=
desc
->
vol
;
struct
ubi_device
*
ubi
=
vol
->
ubi
;
int
err
,
vol_id
=
vol
->
vol_id
;
int
err
;
dbg_msg
(
"erase LEB %d:%d"
,
vol_id
,
lnum
);
dbg_msg
(
"erase LEB %d:%d"
,
vol
->
vol
_id
,
lnum
);
if
(
desc
->
mode
==
UBI_READONLY
||
vol
->
vol_type
==
UBI_STATIC_VOLUME
)
return
-
EROFS
;
...
...
@@ -541,9 +541,8 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum)
{
struct
ubi_volume
*
vol
=
desc
->
vol
;
struct
ubi_device
*
ubi
=
vol
->
ubi
;
int
vol_id
=
vol
->
vol_id
;
dbg_msg
(
"unmap LEB %d:%d"
,
vol_id
,
lnum
);
dbg_msg
(
"unmap LEB %d:%d"
,
vol
->
vol
_id
,
lnum
);
if
(
desc
->
mode
==
UBI_READONLY
||
vol
->
vol_type
==
UBI_STATIC_VOLUME
)
return
-
EROFS
;
...
...
@@ -579,9 +578,8 @@ int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype)
{
struct
ubi_volume
*
vol
=
desc
->
vol
;
struct
ubi_device
*
ubi
=
vol
->
ubi
;
int
vol_id
=
vol
->
vol_id
;
dbg_msg
(
"unmap LEB %d:%d"
,
vol_id
,
lnum
);
dbg_msg
(
"unmap LEB %d:%d"
,
vol
->
vol
_id
,
lnum
);
if
(
desc
->
mode
==
UBI_READONLY
||
vol
->
vol_type
==
UBI_STATIC_VOLUME
)
return
-
EROFS
;
...
...
drivers/mtd/ubi/scan.c
View file @
6208e77e
...
...
@@ -286,9 +286,14 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb,
* FIXME: but this is anyway obsolete and will be removed at
* some point.
*/
dbg_bld
(
"using old crappy leb_ver stuff"
);
if
(
v1
==
v2
)
{
ubi_err
(
"PEB %d and PEB %d have the same version %lld"
,
seb
->
pnum
,
pnum
,
v1
);
return
-
EINVAL
;
}
abs
=
v1
-
v2
;
if
(
abs
<
0
)
abs
=
-
abs
;
...
...
@@ -390,7 +395,6 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb,
vfree
(
buf
);
out_free_vidh:
ubi_free_vid_hdr
(
ubi
,
vh
);
ubi_assert
(
err
<
0
);
return
err
;
}
...
...
@@ -854,7 +858,7 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum
}
vol_id
=
be32_to_cpu
(
vidh
->
vol_id
);
if
(
vol_id
>
UBI_MAX_VOLUMES
&&
vol_id
!=
UBI_LAYOUT_VOL_ID
)
{
if
(
vol_id
>
UBI_MAX_VOLUMES
&&
vol_id
!=
UBI_LAYOUT_VOL
UME
_ID
)
{
int
lnum
=
be32_to_cpu
(
vidh
->
lnum
);
/* Unsupported internal volume */
...
...
drivers/mtd/ubi/ubi.h
View file @
6208e77e
...
...
@@ -144,7 +144,6 @@ struct ubi_volume_desc;
* @readers: number of users holding this volume in read-only mode
* @writers: number of users holding this volume in read-write mode
* @exclusive: whether somebody holds this volume in exclusive mode
* @checked: if this static volume was checked
*
* @reserved_pebs: how many physical eraseblocks are reserved for this volume
* @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
...
...
@@ -152,21 +151,30 @@ struct ubi_volume_desc;
* @used_ebs: how many logical eraseblocks in this volume contain data
* @last_eb_bytes: how many bytes are stored in the last logical eraseblock
* @used_bytes: how many bytes of data this volume contains
* @upd_marker: non-zero if the update marker is set for this volume
* @corrupted: non-zero if the volume is corrupted (static volumes only)
* @alignment: volume alignment
* @data_pad: how many bytes are not used at the end of physical eraseblocks to
* satisfy the requested alignment
* @name_len: volume name length
* @name: volume name
*
* @updating: whether the volume is being updated
* @upd_ebs: how many eraseblocks are expected to be updated
* @upd_bytes: how many bytes are expected to be received
* @upd_received: how many update bytes were already received
* @upd_buf: update buffer which is used to collect update data
* @ch_lnum: LEB number which is being changing by the atomic LEB change
* operation
* @ch_dtype: data persistency type which is being changing by the atomic LEB
* change operation
* @upd_bytes: how many bytes are expected to be received for volume update or
* atomic LEB change
* @upd_received: how many bytes were already received for volume update or
* atomic LEB change
* @upd_buf: update buffer which is used to collect update data or data for
* atomic LEB change
*
* @eba_tbl: EBA table of this volume (LEB->PEB mapping)
* @checked: %1 if this static volume was checked
* @corrupted: %1 if the volume is corrupted (static volumes only)
* @upd_marker: %1 if the update marker is set for this volume
* @updating: %1 if the volume is being updated
* @changing_leb: %1 if the atomic LEB change ioctl command is in progress
*
* @gluebi_desc: gluebi UBI volume descriptor
* @gluebi_refcount: reference count of the gluebi MTD device
...
...
@@ -189,7 +197,6 @@ struct ubi_volume {
int
readers
;
int
writers
;
int
exclusive
;
int
checked
;
int
reserved_pebs
;
int
vol_type
;
...
...
@@ -197,23 +204,31 @@ struct ubi_volume {
int
used_ebs
;
int
last_eb_bytes
;
long
long
used_bytes
;
int
upd_marker
;
int
corrupted
;
int
alignment
;
int
data_pad
;
int
name_len
;
char
name
[
UBI_VOL_NAME_MAX
+
1
];
int
updating
;
int
upd_ebs
;
int
ch_lnum
;
int
ch_dtype
;
long
long
upd_bytes
;
long
long
upd_received
;
void
*
upd_buf
;
int
*
eba_tbl
;
int
checked
:
1
;
int
corrupted
:
1
;
int
upd_marker
:
1
;
int
updating
:
1
;
int
changing_leb
:
1
;
#ifdef CONFIG_MTD_UBI_GLUEBI
/* Gluebi-related stuff may be compiled out */
/*
* Gluebi-related stuff may be compiled out.
* TODO: this should not be built into UBI but should be a separate
* ubimtd driver which works on top of UBI and emulates MTD devices.
*/
struct
ubi_volume_desc
*
gluebi_desc
;
int
gluebi_refcount
;
struct
mtd_info
gluebi_mtd
;
...
...
@@ -250,9 +265,11 @@ struct ubi_wl_entry;
* @rsvd_pebs: count of reserved physical eraseblocks
* @avail_pebs: count of available physical eraseblocks
* @beb_rsvd_pebs: how many physical eraseblocks are reserved for bad PEB
* handling
*
handling
* @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling
*
* @autoresize_vol_id: ID of the volume which has to be auto-resized at the end
* of UBI ititializetion
* @vtbl_slots: how many slots are available in the volume table
* @vtbl_size: size of the volume table in bytes
* @vtbl: in-RAM volume table copy
...
...
@@ -333,12 +350,14 @@ struct ubi_device {
int
beb_rsvd_pebs
;
int
beb_rsvd_level
;
int
autoresize_vol_id
;
int
vtbl_slots
;
int
vtbl_size
;
struct
ubi_vtbl_record
*
vtbl
;
struct
mutex
volumes_mutex
;
int
max_ec
;
/* TODO: mean_ec is not updated run-time, fix */
int
mean_ec
;
/* EBA unit's stuff */
...
...
@@ -399,7 +418,6 @@ struct ubi_device {
#endif
};
extern
struct
kmem_cache
*
ubi_ltree_slab
;
extern
struct
kmem_cache
*
ubi_wl_entry_slab
;
extern
struct
file_operations
ubi_ctrl_cdev_operations
;
extern
struct
file_operations
ubi_cdev_operations
;
...
...
@@ -420,9 +438,14 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol);
void
ubi_free_volume
(
struct
ubi_device
*
ubi
,
struct
ubi_volume
*
vol
);
/* upd.c */
int
ubi_start_update
(
struct
ubi_device
*
ubi
,
int
vol_id
,
long
long
bytes
);
int
ubi_more_update_data
(
struct
ubi_device
*
ubi
,
int
vol_id
,
int
ubi_start_update
(
struct
ubi_device
*
ubi
,
struct
ubi_volume
*
vol
,
long
long
bytes
);
int
ubi_more_update_data
(
struct
ubi_device
*
ubi
,
struct
ubi_volume
*
vol
,
const
void
__user
*
buf
,
int
count
);
int
ubi_start_leb_change
(
struct
ubi_device
*
ubi
,
struct
ubi_volume
*
vol
,
const
struct
ubi_leb_change_req
*
req
);
int
ubi_more_leb_change_data
(
struct
ubi_device
*
ubi
,
struct
ubi_volume
*
vol
,
const
void
__user
*
buf
,
int
count
);
/* misc.c */
int
ubi_calc_data_len
(
const
struct
ubi_device
*
ubi
,
const
void
*
buf
,
int
length
);
...
...
drivers/mtd/ubi/upd.c
View file @
6208e77e
This diff is collapsed.
Click to expand it.
drivers/mtd/ubi/vmt.c
View file @
6208e77e
...
...
@@ -497,8 +497,6 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
dbg_msg
(
"re-size volume %d to from %d to %d PEBs"
,
vol_id
,
vol
->
reserved_pebs
,
reserved_pebs
);
ubi_assert
(
desc
->
mode
==
UBI_EXCLUSIVE
);
ubi_assert
(
vol
==
ubi
->
volumes
[
vol_id
]);
if
(
vol
->
vol_type
==
UBI_STATIC_VOLUME
&&
reserved_pebs
<
vol
->
used_ebs
)
{
...
...
@@ -526,7 +524,6 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
}
spin_unlock
(
&
ubi
->
volumes_lock
);
/* Reserve physical eraseblocks */
pebs
=
reserved_pebs
-
vol
->
reserved_pebs
;
if
(
pebs
>
0
)
{
...
...
@@ -746,11 +743,6 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
goto
fail
;
}
if
(
vol
->
upd_marker
!=
0
&&
vol
->
upd_marker
!=
1
)
{
ubi_err
(
"bad upd_marker"
);
goto
fail
;
}
if
(
vol
->
upd_marker
&&
vol
->
corrupted
)
{
dbg_err
(
"update marker and corrupted simultaneously"
);
goto
fail
;
...
...
@@ -785,7 +777,7 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
n
=
(
long
long
)
vol
->
used_ebs
*
vol
->
usable_leb_size
;
if
(
vol
->
vol_type
==
UBI_DYNAMIC_VOLUME
)
{
if
(
vol
->
corrupted
!=
0
)
{
if
(
vol
->
corrupted
)
{
ubi_err
(
"corrupted dynamic volume"
);
goto
fail
;
}
...
...
@@ -802,10 +794,6 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
goto
fail
;
}
}
else
{
if
(
vol
->
corrupted
!=
0
&&
vol
->
corrupted
!=
1
)
{
ubi_err
(
"bad corrupted"
);
goto
fail
;
}
if
(
vol
->
used_ebs
<
0
||
vol
->
used_ebs
>
vol
->
reserved_pebs
)
{
ubi_err
(
"bad used_ebs"
);
goto
fail
;
...
...
drivers/mtd/ubi/vtbl.c
View file @
6208e77e
...
...
@@ -89,7 +89,7 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
struct
ubi_volume
*
layout_vol
;
ubi_assert
(
idx
>=
0
&&
idx
<
ubi
->
vtbl_slots
);
layout_vol
=
ubi
->
volumes
[
vol_id2idx
(
ubi
,
UBI_LAYOUT_VOL_ID
)];
layout_vol
=
ubi
->
volumes
[
vol_id2idx
(
ubi
,
UBI_LAYOUT_VOL
UME
_ID
)];
if
(
!
vtbl_rec
)
vtbl_rec
=
&
empty_vtbl_record
;
...
...
@@ -111,7 +111,7 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
}
paranoid_vtbl_check
(
ubi
);
return
ubi_wl_flush
(
ubi
)
;
return
0
;
}
/**
...
...
@@ -269,7 +269,7 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si,
* this volume table copy was found during scanning. It has to be wiped
* out.
*/
sv
=
ubi_scan_find_sv
(
si
,
UBI_LAYOUT_VOL_ID
);
sv
=
ubi_scan_find_sv
(
si
,
UBI_LAYOUT_VOL
UME
_ID
);
if
(
sv
)
old_seb
=
ubi_scan_find_seb
(
sv
,
copy
);
...
...
@@ -281,7 +281,7 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si,
}
vid_hdr
->
vol_type
=
UBI_VID_DYNAMIC
;
vid_hdr
->
vol_id
=
cpu_to_be32
(
UBI_LAYOUT_VOL_ID
);
vid_hdr
->
vol_id
=
cpu_to_be32
(
UBI_LAYOUT_VOL
UME
_ID
);
vid_hdr
->
compat
=
UBI_LAYOUT_VOLUME_COMPAT
;
vid_hdr
->
data_size
=
vid_hdr
->
used_ebs
=
vid_hdr
->
data_pad
=
cpu_to_be32
(
0
);
...
...
@@ -514,6 +514,17 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
vol
->
name
[
vol
->
name_len
]
=
'\0'
;
vol
->
vol_id
=
i
;
if
(
vtbl
[
i
].
flags
&
UBI_VTBL_AUTORESIZE_FLG
)
{
/* Auto re-size flag may be set only for one volume */
if
(
ubi
->
autoresize_vol_id
!=
-
1
)
{
ubi_err
(
"more then one auto-resize volume (%d "
"and %d)"
,
ubi
->
autoresize_vol_id
,
i
);
return
-
EINVAL
;
}
ubi
->
autoresize_vol_id
=
i
;
}
ubi_assert
(
!
ubi
->
volumes
[
i
]);
ubi
->
volumes
[
i
]
=
vol
;
ubi
->
vol_count
+=
1
;
...
...
@@ -579,7 +590,7 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
vol
->
last_eb_bytes
=
vol
->
reserved_pebs
;
vol
->
used_bytes
=
(
long
long
)
vol
->
used_ebs
*
(
ubi
->
leb_size
-
vol
->
data_pad
);
vol
->
vol_id
=
UBI_LAYOUT_VOL_ID
;
vol
->
vol_id
=
UBI_LAYOUT_VOL
UME
_ID
;
vol
->
ref_count
=
1
;
ubi_assert
(
!
ubi
->
volumes
[
i
]);
...
...
@@ -732,7 +743,7 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
ubi
->
vtbl_size
=
ubi
->
vtbl_slots
*
UBI_VTBL_RECORD_SIZE
;
ubi
->
vtbl_size
=
ALIGN
(
ubi
->
vtbl_size
,
ubi
->
min_io_size
);
sv
=
ubi_scan_find_sv
(
si
,
UBI_LAYOUT_VOL_ID
);
sv
=
ubi_scan_find_sv
(
si
,
UBI_LAYOUT_VOL
UME
_ID
);
if
(
!
sv
)
{
/*
* No logical eraseblocks belonging to the layout volume were
...
...
drivers/mtd/ubi/wl.c
View file @
6208e77e
...
...
@@ -1303,7 +1303,6 @@ int ubi_wl_flush(struct ubi_device *ubi)
* Make sure all the works which have been done in parallel are
* finished.
*/
ubi_assert
(
ubi
->
ref_count
>
0
);
down_write
(
&
ubi
->
work_sem
);
up_write
(
&
ubi
->
work_sem
);
...
...
include/linux/mtd/ubi.h
View file @
6208e77e
...
...
@@ -25,23 +25,6 @@
#include <linux/types.h>
#include <mtd/ubi-user.h>
/*
* UBI data type hint constants.
*
* UBI_LONGTERM: long-term data
* UBI_SHORTTERM: short-term data
* UBI_UNKNOWN: data persistence is unknown
*
* These constants are used when data is written to UBI volumes in order to
* help the UBI wear-leveling unit to find more appropriate physical
* eraseblocks.
*/
enum
{
UBI_LONGTERM
=
1
,
UBI_SHORTTERM
,
UBI_UNKNOWN
};
/*
* enum ubi_open_mode - UBI volume open mode constants.
*
...
...
include/mtd/ubi-header.h
View file @
6208e77e
...
...
@@ -57,6 +57,43 @@ enum {
UBI_VID_STATIC
=
2
};
/*
* Volume flags used in the volume table record.
*
* @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume
*
* %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume
* table. UBI automatically re-sizes the volume which has this flag and makes
* the volume to be of largest possible size. This means that if after the
* initialization UBI finds out that there are available physical eraseblocks
* present on the device, it automatically appends all of them to the volume
* (the physical eraseblocks reserved for bad eraseblocks handling and other
* reserved physical eraseblocks are not taken). So, if there is a volume with
* the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical
* eraseblocks will be zero after UBI is loaded, because all of them will be
* reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared
* after the volume had been initialized.
*
* The auto-resize feature is useful for device production purposes. For
* example, different NAND flash chips may have different amount of initial bad
* eraseblocks, depending of particular chip instance. Manufacturers of NAND
* chips usually guarantee that the amount of initial bad eraseblocks does not
* exceed certain percent, e.g. 2%. When one creates an UBI image which will be
* flashed to the end devices in production, he does not know the exact amount
* of good physical eraseblocks the NAND chip on the device will have, but this
* number is required to calculate the volume sized and put them to the volume
* table of the UBI image. In this case, one of the volumes (e.g., the one
* which will store the root file system) is marked as "auto-resizable", and
* UBI will adjust its size on the first boot if needed.
*
* Note, first UBI reserves some amount of physical eraseblocks for bad
* eraseblock handling, and then re-sizes the volume, not vice-versa. This
* means that the pool of reserved physical eraseblocks will always be present.
*/
enum
{
UBI_VTBL_AUTORESIZE_FLG
=
0x01
,
};
/*
* Compatibility constants used by internal volumes.
*
...
...
@@ -262,7 +299,9 @@ struct ubi_vid_hdr {
/* The layout volume contains the volume table */
#define UBI_LAYOUT_VOL_ID UBI_INTERNAL_VOL_START
#define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START
#define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC
#define UBI_LAYOUT_VOLUME_ALIGN 1
#define UBI_LAYOUT_VOLUME_EBS 2
#define UBI_LAYOUT_VOLUME_NAME "layout volume"
#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT
...
...
@@ -289,7 +328,8 @@ struct ubi_vid_hdr {
* @upd_marker: if volume update was started but not finished
* @name_len: volume name length
* @name: the volume name
* @padding2: reserved, zeroes
* @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG)
* @padding: reserved, zeroes
* @crc: a CRC32 checksum of the record
*
* The volume table records are stored in the volume table, which is stored in
...
...
@@ -324,7 +364,8 @@ struct ubi_vtbl_record {
__u8
upd_marker
;
__be16
name_len
;
__u8
name
[
UBI_VOL_NAME_MAX
+
1
];
__u8
padding2
[
24
];
__u8
flags
;
__u8
padding
[
23
];
__be32
crc
;
}
__attribute__
((
packed
));
...
...
include/mtd/ubi-user.h
View file @
6208e77e
...
...
@@ -63,7 +63,7 @@
*
* Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the
* corresponding UBI volume character device. A pointer to a 64-bit update
* size should be passed to the IOCTL. After th
en
, UBI expects user to write
* size should be passed to the IOCTL. After th
is
, UBI expects user to write
* this number of bytes to the volume character device. The update is finished
* when the claimed number of bytes is passed. So, the volume update sequence
* is something like:
...
...
@@ -72,6 +72,15 @@
* ioctl(fd, UBI_IOCVOLUP, &image_size);
* write(fd, buf, image_size);
* close(fd);
*
* Atomic eraseblock change
* ~~~~~~~~~~~~~~~~~~~~~~~~
*
* Atomic eraseblock change operation is done via the %UBI_IOCEBCH IOCTL
* command of the corresponding UBI volume character device. A pointer to
* &struct ubi_leb_change_req has to be passed to the IOCTL. Then the user is
* expected to write the requested amount of bytes. This is similar to the
* "volume update" IOCTL.
*/
/*
...
...
@@ -113,10 +122,29 @@
#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t)
/* An eraseblock erasure command, used for debugging, disabled by default */
#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t)
/* An atomic eraseblock change command */
#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t)
/* Maximum MTD device name length supported by UBI */
#define MAX_UBI_MTD_NAME_LEN 127
/*
* UBI data type hint constants.
*
* UBI_LONGTERM: long-term data
* UBI_SHORTTERM: short-term data
* UBI_UNKNOWN: data persistence is unknown
*
* These constants are used when data is written to UBI volumes in order to
* help the UBI wear-leveling unit to find more appropriate physical
* eraseblocks.
*/
enum
{
UBI_LONGTERM
=
1
,
UBI_SHORTTERM
=
2
,
UBI_UNKNOWN
=
3
,
};
/*
* UBI volume type constants.
*
...
...
@@ -125,7 +153,7 @@
*/
enum
{
UBI_DYNAMIC_VOLUME
=
3
,
UBI_STATIC_VOLUME
=
4
,
UBI_STATIC_VOLUME
=
4
,
};
/**
...
...
@@ -137,7 +165,7 @@ enum {
*
* This data structure is used to specify MTD device UBI has to attach and the
* parameters it has to use. The number which should be assigned to the new UBI
* device is passed in @ubi_num. UBI may automatically assi
ng
the number if
* device is passed in @ubi_num. UBI may automatically assi
gn
the number if
* @UBI_DEV_NUM_AUTO is passed. In this case, the device number is returned in
* @ubi_num.
*
...
...
@@ -176,7 +204,7 @@ struct ubi_attach_req {
* @padding2: reserved for future, not used, has to be zeroed
* @name: volume name
*
* This structure is used by userspace programs when creating new volumes. The
* This structure is used by user
-
space programs when creating new volumes. The
* @used_bytes field is only necessary when creating static volumes.
*
* The @alignment field specifies the required alignment of the volume logical
...
...
@@ -222,4 +250,19 @@ struct ubi_rsvol_req {
int32_t
vol_id
;
}
__attribute__
((
packed
));
/**
* struct ubi_leb_change_req - a data structure used in atomic logical
* eraseblock change requests.
* @lnum: logical eraseblock number to change
* @bytes: how many bytes will be written to the logical eraseblock
* @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN)
* @padding: reserved for future, not used, has to be zeroed
*/
struct
ubi_leb_change_req
{
int32_t
lnum
;
int32_t
bytes
;
uint8_t
dtype
;
uint8_t
padding
[
7
];
}
__attribute__
((
packed
));
#endif
/* __UBI_USER_H__ */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment