2005-04-17 02:20:36 +04:00
/*
* JFFS2 - - Journalling Flash File System , Version 2.
*
2007-04-25 17:16:47 +04:00
* Copyright © 2001 - 2007 Red Hat , Inc .
2010-08-08 17:15:22 +04:00
* Copyright © 2004 - 2010 David Woodhouse < dwmw2 @ infradead . org >
2007-04-25 17:16:47 +04:00
* Copyright © 2004 Ferenc Havasi < havasi @ inf . u - szeged . hu > ,
2007-07-10 13:01:22 +04:00
* University of Szeged , Hungary
2005-04-17 02:20:36 +04:00
*
2010-08-08 17:15:22 +04:00
* Created by Arjan van de Ven < arjan @ infradead . org >
*
2005-04-17 02:20:36 +04:00
* For licensing information , see the file ' LICENCE ' in this directory .
*
*/
2012-02-16 03:56:45 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-17 02:20:36 +04:00
# include "compr.h"
static DEFINE_SPINLOCK ( jffs2_compressor_list_lock ) ;
/* Available compressors are on this list */
static LIST_HEAD ( jffs2_compressor_list ) ;
/* Actual compression mode */
static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY ;
/* Statistics for blocks stored without compression */
static uint32_t none_stat_compr_blocks = 0 , none_stat_decompr_blocks = 0 , none_stat_compr_size = 0 ;
2007-07-10 13:28:42 +04:00
/*
* Return 1 to use this compression
*/
static int jffs2_is_best_compression ( struct jffs2_compressor * this ,
struct jffs2_compressor * best , uint32_t size , uint32_t bestsize )
{
switch ( jffs2_compression_mode ) {
case JFFS2_COMPR_MODE_SIZE :
if ( bestsize > size )
return 1 ;
return 0 ;
case JFFS2_COMPR_MODE_FAVOURLZO :
if ( ( this - > compr = = JFFS2_COMPR_LZO ) & & ( bestsize > size ) )
return 1 ;
if ( ( best - > compr ! = JFFS2_COMPR_LZO ) & & ( bestsize > size ) )
return 1 ;
if ( ( this - > compr = = JFFS2_COMPR_LZO ) & & ( bestsize > ( size * FAVOUR_LZO_PERCENT / 100 ) ) )
return 1 ;
if ( ( bestsize * FAVOUR_LZO_PERCENT / 100 ) > size )
return 1 ;
return 0 ;
}
/* Shouldn't happen */
return 0 ;
}
2011-10-17 05:15:23 +04:00
/*
* jffs2_selected_compress :
* @ compr : Explicit compression type to use ( ie , JFFS2_COMPR_ZLIB ) .
* If 0 , just take the first available compression mode .
* @ data_in : Pointer to uncompressed data
* @ cpage_out : Pointer to returned pointer to buffer for compressed data
* @ datalen : On entry , holds the amount of data available for compression .
* On exit , expected to hold the amount of data actually compressed .
* @ cdatalen : On entry , holds the amount of space available for compressed
* data . On exit , expected to hold the actual size of the compressed
* data .
*
* Returns : the compression type used . Zero is used to show that the data
* could not be compressed ; probably because we couldn ' t find the requested
* compression mode .
*/
static int jffs2_selected_compress ( u8 compr , unsigned char * data_in ,
unsigned char * * cpage_out , u32 * datalen , u32 * cdatalen )
{
struct jffs2_compressor * this ;
int err , ret = JFFS2_COMPR_NONE ;
uint32_t orig_slen , orig_dlen ;
char * output_buf ;
output_buf = kmalloc ( * cdatalen , GFP_KERNEL ) ;
if ( ! output_buf ) {
2012-02-16 03:56:45 +04:00
pr_warn ( " No memory for compressor allocation. Compression failed. \n " ) ;
2011-10-17 05:15:23 +04:00
return ret ;
}
orig_slen = * datalen ;
orig_dlen = * cdatalen ;
spin_lock ( & jffs2_compressor_list_lock ) ;
list_for_each_entry ( this , & jffs2_compressor_list , list ) {
/* Skip decompress-only and disabled modules */
if ( ! this - > compress | | this - > disabled )
continue ;
/* Skip if not the desired compression type */
if ( compr & & ( compr ! = this - > compr ) )
continue ;
/*
* Either compression type was unspecified , or we found our
* compressor ; either way , we ' re good to go .
*/
this - > usecount + + ;
spin_unlock ( & jffs2_compressor_list_lock ) ;
* datalen = orig_slen ;
* cdatalen = orig_dlen ;
err = this - > compress ( data_in , output_buf , datalen , cdatalen ) ;
spin_lock ( & jffs2_compressor_list_lock ) ;
this - > usecount - - ;
if ( ! err ) {
/* Success */
ret = this - > compr ;
this - > stat_compr_blocks + + ;
this - > stat_compr_orig_size + = * datalen ;
this - > stat_compr_new_size + = * cdatalen ;
break ;
}
}
spin_unlock ( & jffs2_compressor_list_lock ) ;
if ( ret = = JFFS2_COMPR_NONE )
kfree ( output_buf ) ;
else
* cpage_out = output_buf ;
return ret ;
}
2005-04-17 02:20:36 +04:00
/* jffs2_compress:
2008-10-17 18:19:45 +04:00
* @ data_in : Pointer to uncompressed data
* @ cpage_out : Pointer to returned pointer to buffer for compressed data
2005-04-17 02:20:36 +04:00
* @ datalen : On entry , holds the amount of data available for compression .
* On exit , expected to hold the amount of data actually compressed .
* @ cdatalen : On entry , holds the amount of space available for compressed
* data . On exit , expected to hold the actual size of the compressed
* data .
*
* Returns : Lower byte to be stored with data indicating compression type used .
2005-11-07 14:16:07 +03:00
* Zero is used to show that the data could not be compressed - the
2005-04-17 02:20:36 +04:00
* compressed version was actually larger than the original .
* Upper byte will be used later . ( soon )
*
* If the cdata buffer isn ' t large enough to hold all the uncompressed data ,
2005-11-07 14:16:07 +03:00
* jffs2_compress should compress as much as will fit , and should set
2005-04-17 02:20:36 +04:00
* * datalen accordingly to show the amount of data which were compressed .
*/
uint16_t jffs2_compress ( struct jffs2_sb_info * c , struct jffs2_inode_info * f ,
2007-07-10 13:01:22 +04:00
unsigned char * data_in , unsigned char * * cpage_out ,
uint32_t * datalen , uint32_t * cdatalen )
2005-04-17 02:20:36 +04:00
{
int ret = JFFS2_COMPR_NONE ;
2011-10-17 05:15:16 +04:00
int mode , compr_ret ;
2007-07-10 13:01:22 +04:00
struct jffs2_compressor * this , * best = NULL ;
unsigned char * output_buf = NULL , * tmp_buf ;
uint32_t orig_slen , orig_dlen ;
uint32_t best_slen = 0 , best_dlen = 0 ;
2005-04-17 02:20:36 +04:00
2011-10-17 05:15:16 +04:00
if ( c - > mount_opts . override_compr )
mode = c - > mount_opts . compr ;
else
mode = jffs2_compression_mode ;
switch ( mode ) {
2007-07-10 13:01:22 +04:00
case JFFS2_COMPR_MODE_NONE :
break ;
case JFFS2_COMPR_MODE_PRIORITY :
2011-10-17 05:15:23 +04:00
ret = jffs2_selected_compress ( 0 , data_in , cpage_out , datalen ,
cdatalen ) ;
2007-07-10 13:01:22 +04:00
break ;
case JFFS2_COMPR_MODE_SIZE :
2007-07-10 13:28:42 +04:00
case JFFS2_COMPR_MODE_FAVOURLZO :
2007-07-10 13:01:22 +04:00
orig_slen = * datalen ;
orig_dlen = * cdatalen ;
spin_lock ( & jffs2_compressor_list_lock ) ;
list_for_each_entry ( this , & jffs2_compressor_list , list ) {
/* Skip decompress-only backwards-compatibility and disabled modules */
if ( ( ! this - > compress ) | | ( this - > disabled ) )
continue ;
/* Allocating memory for output buffer if necessary */
2007-07-10 13:28:42 +04:00
if ( ( this - > compr_buf_size < orig_slen ) & & ( this - > compr_buf ) ) {
2007-07-10 13:01:22 +04:00
spin_unlock ( & jffs2_compressor_list_lock ) ;
kfree ( this - > compr_buf ) ;
spin_lock ( & jffs2_compressor_list_lock ) ;
this - > compr_buf_size = 0 ;
this - > compr_buf = NULL ;
}
if ( ! this - > compr_buf ) {
spin_unlock ( & jffs2_compressor_list_lock ) ;
2007-07-10 13:28:42 +04:00
tmp_buf = kmalloc ( orig_slen , GFP_KERNEL ) ;
2007-07-10 13:01:22 +04:00
spin_lock ( & jffs2_compressor_list_lock ) ;
if ( ! tmp_buf ) {
2012-02-16 03:56:45 +04:00
pr_warn ( " No memory for compressor allocation. (%d bytes) \n " ,
2012-02-16 03:56:44 +04:00
orig_slen ) ;
2007-07-10 13:01:22 +04:00
continue ;
}
else {
this - > compr_buf = tmp_buf ;
2007-07-10 13:28:42 +04:00
this - > compr_buf_size = orig_slen ;
2007-07-10 13:01:22 +04:00
}
}
this - > usecount + + ;
spin_unlock ( & jffs2_compressor_list_lock ) ;
* datalen = orig_slen ;
* cdatalen = orig_dlen ;
2010-09-23 06:52:33 +04:00
compr_ret = this - > compress ( data_in , this - > compr_buf , datalen , cdatalen ) ;
2007-07-10 13:01:22 +04:00
spin_lock ( & jffs2_compressor_list_lock ) ;
this - > usecount - - ;
if ( ! compr_ret ) {
2007-07-10 13:28:42 +04:00
if ( ( ( ! best_dlen ) | | jffs2_is_best_compression ( this , best , * cdatalen , best_dlen ) )
& & ( * cdatalen < * datalen ) ) {
2007-07-10 13:01:22 +04:00
best_dlen = * cdatalen ;
best_slen = * datalen ;
best = this ;
}
}
}
if ( best_dlen ) {
* cdatalen = best_dlen ;
* datalen = best_slen ;
output_buf = best - > compr_buf ;
best - > compr_buf = NULL ;
best - > compr_buf_size = 0 ;
best - > stat_compr_blocks + + ;
best - > stat_compr_orig_size + = best_slen ;
best - > stat_compr_new_size + = best_dlen ;
ret = best - > compr ;
2011-10-17 05:15:23 +04:00
* cpage_out = output_buf ;
2007-07-10 13:01:22 +04:00
}
spin_unlock ( & jffs2_compressor_list_lock ) ;
break ;
2011-10-17 05:15:23 +04:00
case JFFS2_COMPR_MODE_FORCELZO :
ret = jffs2_selected_compress ( JFFS2_COMPR_LZO , data_in ,
cpage_out , datalen , cdatalen ) ;
break ;
case JFFS2_COMPR_MODE_FORCEZLIB :
ret = jffs2_selected_compress ( JFFS2_COMPR_ZLIB , data_in ,
cpage_out , datalen , cdatalen ) ;
break ;
2007-07-10 13:01:22 +04:00
default :
2012-02-16 03:56:45 +04:00
pr_err ( " unknown compression mode \n " ) ;
2007-07-10 13:01:22 +04:00
}
2011-10-17 05:15:23 +04:00
2007-07-10 13:01:22 +04:00
if ( ret = = JFFS2_COMPR_NONE ) {
* cpage_out = data_in ;
* datalen = * cdatalen ;
none_stat_compr_blocks + + ;
none_stat_compr_size + = * datalen ;
}
2005-04-17 02:20:36 +04:00
return ret ;
}
int jffs2_decompress ( struct jffs2_sb_info * c , struct jffs2_inode_info * f ,
2005-11-07 14:16:07 +03:00
uint16_t comprtype , unsigned char * cdata_in ,
2005-04-17 02:20:36 +04:00
unsigned char * data_out , uint32_t cdatalen , uint32_t datalen )
{
2007-07-10 13:01:22 +04:00
struct jffs2_compressor * this ;
int ret ;
2005-04-17 02:20:36 +04:00
/* Older code had a bug where it would write non-zero 'usercompr'
fields . Deal with it . */
if ( ( comprtype & 0xff ) < = JFFS2_COMPR_ZLIB )
comprtype & = 0xff ;
switch ( comprtype & 0xff ) {
case JFFS2_COMPR_NONE :
/* This should be special-cased elsewhere, but we might as well deal with it */
memcpy ( data_out , cdata_in , datalen ) ;
2007-07-10 13:01:22 +04:00
none_stat_decompr_blocks + + ;
2005-04-17 02:20:36 +04:00
break ;
case JFFS2_COMPR_ZERO :
memset ( data_out , 0 , datalen ) ;
break ;
default :
2007-07-10 13:01:22 +04:00
spin_lock ( & jffs2_compressor_list_lock ) ;
list_for_each_entry ( this , & jffs2_compressor_list , list ) {
if ( comprtype = = this - > compr ) {
this - > usecount + + ;
spin_unlock ( & jffs2_compressor_list_lock ) ;
2010-09-23 06:52:33 +04:00
ret = this - > decompress ( cdata_in , data_out , cdatalen , datalen ) ;
2007-07-10 13:01:22 +04:00
spin_lock ( & jffs2_compressor_list_lock ) ;
if ( ret ) {
2012-02-16 03:56:44 +04:00
pr_warn ( " Decompressor \" %s \" returned %d \n " ,
this - > name , ret ) ;
2007-07-10 13:01:22 +04:00
}
else {
this - > stat_decompr_blocks + + ;
}
this - > usecount - - ;
spin_unlock ( & jffs2_compressor_list_lock ) ;
return ret ;
}
}
2012-02-16 03:56:45 +04:00
pr_warn ( " compression type 0x%02x not available \n " , comprtype ) ;
2007-07-10 13:01:22 +04:00
spin_unlock ( & jffs2_compressor_list_lock ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
return 0 ;
}
int jffs2_register_compressor ( struct jffs2_compressor * comp )
{
2007-07-10 13:01:22 +04:00
struct jffs2_compressor * this ;
2005-04-17 02:20:36 +04:00
2007-07-10 13:01:22 +04:00
if ( ! comp - > name ) {
2012-02-16 03:56:44 +04:00
pr_warn ( " NULL compressor name at registering JFFS2 compressor. Failed. \n " ) ;
2007-07-10 13:01:22 +04:00
return - 1 ;
}
comp - > compr_buf_size = 0 ;
comp - > compr_buf = NULL ;
comp - > usecount = 0 ;
comp - > stat_compr_orig_size = 0 ;
comp - > stat_compr_new_size = 0 ;
comp - > stat_compr_blocks = 0 ;
comp - > stat_decompr_blocks = 0 ;
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Registering JFFS2 compressor \" %s \" \n " , comp - > name ) ;
2005-04-17 02:20:36 +04:00
2007-07-10 13:01:22 +04:00
spin_lock ( & jffs2_compressor_list_lock ) ;
2005-04-17 02:20:36 +04:00
2007-07-10 13:01:22 +04:00
list_for_each_entry ( this , & jffs2_compressor_list , list ) {
if ( this - > priority < comp - > priority ) {
list_add ( & comp - > list , this - > list . prev ) ;
goto out ;
}
}
list_add_tail ( & comp - > list , & jffs2_compressor_list ) ;
2005-04-17 02:20:36 +04:00
out :
2007-07-10 13:01:22 +04:00
D2 ( list_for_each_entry ( this , & jffs2_compressor_list , list ) {
printk ( KERN_DEBUG " Compressor \" %s \" , prio %d \n " , this - > name , this - > priority ) ;
} )
2005-04-17 02:20:36 +04:00
2007-07-10 13:01:22 +04:00
spin_unlock ( & jffs2_compressor_list_lock ) ;
2005-04-17 02:20:36 +04:00
2007-07-10 13:01:22 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
int jffs2_unregister_compressor ( struct jffs2_compressor * comp )
{
2012-02-16 03:56:43 +04:00
D2 ( struct jffs2_compressor * this ) ;
2005-04-17 02:20:36 +04:00
2012-02-16 03:56:43 +04:00
jffs2_dbg ( 1 , " Unregistering JFFS2 compressor \" %s \" \n " , comp - > name ) ;
2005-04-17 02:20:36 +04:00
2007-07-10 13:01:22 +04:00
spin_lock ( & jffs2_compressor_list_lock ) ;
2005-04-17 02:20:36 +04:00
2007-07-10 13:01:22 +04:00
if ( comp - > usecount ) {
spin_unlock ( & jffs2_compressor_list_lock ) ;
2012-02-16 03:56:45 +04:00
pr_warn ( " Compressor module is in use. Unregister failed. \n " ) ;
2007-07-10 13:01:22 +04:00
return - 1 ;
}
list_del ( & comp - > list ) ;
2005-04-17 02:20:36 +04:00
2007-07-10 13:01:22 +04:00
D2 ( list_for_each_entry ( this , & jffs2_compressor_list , list ) {
printk ( KERN_DEBUG " Compressor \" %s \" , prio %d \n " , this - > name , this - > priority ) ;
} )
spin_unlock ( & jffs2_compressor_list_lock ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
void jffs2_free_comprbuf ( unsigned char * comprbuf , unsigned char * orig )
{
2007-07-10 13:01:22 +04:00
if ( orig ! = comprbuf )
kfree ( comprbuf ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-16 19:08:10 +04:00
int __init jffs2_compressors_init ( void )
2005-04-17 02:20:36 +04:00
{
/* Registering compressors */
# ifdef CONFIG_JFFS2_ZLIB
2007-07-10 13:01:22 +04:00
jffs2_zlib_init ( ) ;
2005-04-17 02:20:36 +04:00
# endif
# ifdef CONFIG_JFFS2_RTIME
2007-07-10 13:01:22 +04:00
jffs2_rtime_init ( ) ;
2005-04-17 02:20:36 +04:00
# endif
# ifdef CONFIG_JFFS2_RUBIN
2007-07-10 13:01:22 +04:00
jffs2_rubinmips_init ( ) ;
jffs2_dynrubin_init ( ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-07-10 13:28:36 +04:00
# ifdef CONFIG_JFFS2_LZO
jffs2_lzo_init ( ) ;
# endif
2005-04-17 02:20:36 +04:00
/* Setting default compression mode */
# ifdef CONFIG_JFFS2_CMODE_NONE
2007-07-10 13:01:22 +04:00
jffs2_compression_mode = JFFS2_COMPR_MODE_NONE ;
2012-02-16 03:56:45 +04:00
jffs2_dbg ( 1 , " default compression mode: none \n " ) ;
2005-04-17 02:20:36 +04:00
# else
# ifdef CONFIG_JFFS2_CMODE_SIZE
2007-07-10 13:01:22 +04:00
jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE ;
2012-02-16 03:56:45 +04:00
jffs2_dbg ( 1 , " default compression mode: size \n " ) ;
2007-07-10 13:28:42 +04:00
# else
# ifdef CONFIG_JFFS2_CMODE_FAVOURLZO
jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO ;
2012-02-16 03:56:45 +04:00
jffs2_dbg ( 1 , " default compression mode: favourlzo \n " ) ;
2005-04-17 02:20:36 +04:00
# else
2012-02-16 03:56:45 +04:00
jffs2_dbg ( 1 , " default compression mode: priority \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-07-10 13:28:42 +04:00
# endif
2005-04-17 02:20:36 +04:00
# endif
2007-07-10 13:01:22 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-06-03 03:25:50 +04:00
int jffs2_compressors_exit ( void )
2005-04-17 02:20:36 +04:00
{
/* Unregistering compressors */
2007-07-10 13:28:36 +04:00
# ifdef CONFIG_JFFS2_LZO
jffs2_lzo_exit ( ) ;
# endif
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_JFFS2_RUBIN
2007-07-10 13:01:22 +04:00
jffs2_dynrubin_exit ( ) ;
jffs2_rubinmips_exit ( ) ;
2005-04-17 02:20:36 +04:00
# endif
# ifdef CONFIG_JFFS2_RTIME
2007-07-10 13:01:22 +04:00
jffs2_rtime_exit ( ) ;
2005-04-17 02:20:36 +04:00
# endif
# ifdef CONFIG_JFFS2_ZLIB
2007-07-10 13:01:22 +04:00
jffs2_zlib_exit ( ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-07-10 13:01:22 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}