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
nexedi
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