2005-04-17 02:20:36 +04:00
/*
* JFFS2 - - Journalling Flash File System , Version 2.
*
* Copyright ( C ) 2001 - 2003 Red Hat , Inc .
* Created by Arjan van de Ven < arjanv @ redhat . com >
*
* Copyright ( C ) 2004 Ferenc Havasi < havasi @ inf . u - szeged . hu > ,
* University of Szeged , Hungary
*
* For licensing information , see the file ' LICENCE ' in this directory .
*
2005-07-26 17:24:43 +04:00
* $ Id : compr . c , v 1.45 2005 / 07 / 26 13 : 24 : 40 havasi Exp $
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 ;
/* jffs2_compress:
* @ data : Pointer to uncompressed data
* @ cdata : 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 : Lower byte to be stored with data indicating compression type used .
* Zero is used to show that the data could not be compressed - the
* 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 ,
* jffs2_compress should compress as much as will fit , and should set
* * 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 ,
unsigned char * data_in , unsigned char * * cpage_out ,
uint32_t * datalen , uint32_t * cdatalen )
{
int ret = JFFS2_COMPR_NONE ;
int compr_ret ;
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 ;
switch ( jffs2_compression_mode ) {
case JFFS2_COMPR_MODE_NONE :
break ;
case JFFS2_COMPR_MODE_PRIORITY :
output_buf = kmalloc ( * cdatalen , GFP_KERNEL ) ;
if ( ! output_buf ) {
printk ( KERN_WARNING " JFFS2: No memory for compressor allocation. Compression failed. \n " ) ;
goto out ;
}
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 ;
this - > usecount + + ;
spin_unlock ( & jffs2_compressor_list_lock ) ;
* datalen = orig_slen ;
* cdatalen = orig_dlen ;
compr_ret = this - > compress ( data_in , output_buf , datalen , cdatalen , NULL ) ;
spin_lock ( & jffs2_compressor_list_lock ) ;
this - > usecount - - ;
if ( ! compr_ret ) {
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 ) ;
break ;
case JFFS2_COMPR_MODE_SIZE :
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 */
if ( ( this - > compr_buf_size < orig_dlen ) & & ( this - > compr_buf ) ) {
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 ) ;
tmp_buf = kmalloc ( orig_dlen , GFP_KERNEL ) ;
spin_lock ( & jffs2_compressor_list_lock ) ;
if ( ! tmp_buf ) {
printk ( KERN_WARNING " JFFS2: No memory for compressor allocation. (%d bytes) \n " , orig_dlen ) ;
continue ;
}
else {
this - > compr_buf = tmp_buf ;
this - > compr_buf_size = orig_dlen ;
}
}
this - > usecount + + ;
spin_unlock ( & jffs2_compressor_list_lock ) ;
* datalen = orig_slen ;
* cdatalen = orig_dlen ;
compr_ret = this - > compress ( data_in , this - > compr_buf , datalen , cdatalen , NULL ) ;
spin_lock ( & jffs2_compressor_list_lock ) ;
this - > usecount - - ;
if ( ! compr_ret ) {
if ( ( ! best_dlen ) | | ( best_dlen > * cdatalen ) ) {
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 ;
}
spin_unlock ( & jffs2_compressor_list_lock ) ;
break ;
default :
printk ( KERN_ERR " JFFS2: unknow compression mode. \n " ) ;
}
out :
if ( ret = = JFFS2_COMPR_NONE ) {
* cpage_out = data_in ;
* datalen = * cdatalen ;
none_stat_compr_blocks + + ;
none_stat_compr_size + = * datalen ;
}
else {
* cpage_out = output_buf ;
}
return ret ;
}
int jffs2_decompress ( struct jffs2_sb_info * c , struct jffs2_inode_info * f ,
uint16_t comprtype , unsigned char * cdata_in ,
unsigned char * data_out , uint32_t cdatalen , uint32_t datalen )
{
struct jffs2_compressor * this ;
int ret ;
/* 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 ) ;
none_stat_decompr_blocks + + ;
break ;
case JFFS2_COMPR_ZERO :
memset ( data_out , 0 , datalen ) ;
break ;
default :
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 ) ;
ret = this - > decompress ( cdata_in , data_out , cdatalen , datalen , NULL ) ;
spin_lock ( & jffs2_compressor_list_lock ) ;
if ( ret ) {
printk ( KERN_WARNING " Decompressor \" %s \" returned %d \n " , this - > name , ret ) ;
}
else {
this - > stat_decompr_blocks + + ;
}
this - > usecount - - ;
spin_unlock ( & jffs2_compressor_list_lock ) ;
return ret ;
}
}
printk ( KERN_WARNING " JFFS2 compression type 0x%02x not available. \n " , comprtype ) ;
spin_unlock ( & jffs2_compressor_list_lock ) ;
return - EIO ;
}
return 0 ;
}
int jffs2_register_compressor ( struct jffs2_compressor * comp )
{
struct jffs2_compressor * this ;
if ( ! comp - > name ) {
printk ( KERN_WARNING " NULL compressor name at registering JFFS2 compressor. Failed. \n " ) ;
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 ;
D1 ( printk ( KERN_DEBUG " Registering JFFS2 compressor \" %s \" \n " , comp - > name ) ) ;
spin_lock ( & jffs2_compressor_list_lock ) ;
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 ) ;
out :
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 ;
}
int jffs2_unregister_compressor ( struct jffs2_compressor * comp )
{
D2 ( struct jffs2_compressor * this ; )
D1 ( printk ( KERN_DEBUG " Unregistering JFFS2 compressor \" %s \" \n " , comp - > name ) ) ;
spin_lock ( & jffs2_compressor_list_lock ) ;
if ( comp - > usecount ) {
spin_unlock ( & jffs2_compressor_list_lock ) ;
printk ( KERN_WARNING " JFFS2: Compressor modul is in use. Unregister failed. \n " ) ;
return - 1 ;
}
list_del ( & comp - > list ) ;
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 ;
}
# ifdef CONFIG_JFFS2_PROC
# define JFFS2_STAT_BUF_SIZE 16000
char * jffs2_list_compressors ( void )
{
struct jffs2_compressor * this ;
char * buf , * act_buf ;
act_buf = buf = kmalloc ( JFFS2_STAT_BUF_SIZE , GFP_KERNEL ) ;
list_for_each_entry ( this , & jffs2_compressor_list , list ) {
act_buf + = sprintf ( act_buf , " %10s priority:%d " , this - > name , this - > priority ) ;
if ( ( this - > disabled ) | | ( ! this - > compress ) )
act_buf + = sprintf ( act_buf , " disabled " ) ;
else
act_buf + = sprintf ( act_buf , " enabled " ) ;
act_buf + = sprintf ( act_buf , " \n " ) ;
}
return buf ;
}
char * jffs2_stats ( void )
{
struct jffs2_compressor * this ;
char * buf , * act_buf ;
act_buf = buf = kmalloc ( JFFS2_STAT_BUF_SIZE , GFP_KERNEL ) ;
act_buf + = sprintf ( act_buf , " JFFS2 compressor statistics: \n " ) ;
act_buf + = sprintf ( act_buf , " %10s " , " none " ) ;
act_buf + = sprintf ( act_buf , " compr: %d blocks (%d) decompr: %d blocks \n " , none_stat_compr_blocks ,
none_stat_compr_size , none_stat_decompr_blocks ) ;
spin_lock ( & jffs2_compressor_list_lock ) ;
list_for_each_entry ( this , & jffs2_compressor_list , list ) {
act_buf + = sprintf ( act_buf , " %10s " , this - > name ) ;
if ( ( this - > disabled ) | | ( ! this - > compress ) )
act_buf + = sprintf ( act_buf , " - " ) ;
else
act_buf + = sprintf ( act_buf , " + " ) ;
act_buf + = sprintf ( act_buf , " compr: %d blocks (%d/%d) decompr: %d blocks " , this - > stat_compr_blocks ,
this - > stat_compr_new_size , this - > stat_compr_orig_size ,
this - > stat_decompr_blocks ) ;
act_buf + = sprintf ( act_buf , " \n " ) ;
}
spin_unlock ( & jffs2_compressor_list_lock ) ;
return buf ;
}
char * jffs2_get_compression_mode_name ( void )
{
switch ( jffs2_compression_mode ) {
case JFFS2_COMPR_MODE_NONE :
return " none " ;
case JFFS2_COMPR_MODE_PRIORITY :
return " priority " ;
case JFFS2_COMPR_MODE_SIZE :
return " size " ;
}
return " unkown " ;
}
int jffs2_set_compression_mode_name ( const char * name )
{
if ( ! strcmp ( " none " , name ) ) {
jffs2_compression_mode = JFFS2_COMPR_MODE_NONE ;
return 0 ;
}
if ( ! strcmp ( " priority " , name ) ) {
jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY ;
return 0 ;
}
if ( ! strcmp ( " size " , name ) ) {
jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE ;
return 0 ;
}
return 1 ;
}
static int jffs2_compressor_Xable ( const char * name , int disabled )
{
struct jffs2_compressor * this ;
spin_lock ( & jffs2_compressor_list_lock ) ;
list_for_each_entry ( this , & jffs2_compressor_list , list ) {
if ( ! strcmp ( this - > name , name ) ) {
this - > disabled = disabled ;
spin_unlock ( & jffs2_compressor_list_lock ) ;
return 0 ;
}
}
spin_unlock ( & jffs2_compressor_list_lock ) ;
printk ( KERN_WARNING " JFFS2: compressor %s not found. \n " , name ) ;
return 1 ;
}
int jffs2_enable_compressor_name ( const char * name )
{
return jffs2_compressor_Xable ( name , 0 ) ;
}
int jffs2_disable_compressor_name ( const char * name )
{
return jffs2_compressor_Xable ( name , 1 ) ;
}
int jffs2_set_compressor_priority ( const char * name , int priority )
{
struct jffs2_compressor * this , * comp ;
spin_lock ( & jffs2_compressor_list_lock ) ;
list_for_each_entry ( this , & jffs2_compressor_list , list ) {
if ( ! strcmp ( this - > name , name ) ) {
this - > priority = priority ;
comp = this ;
goto reinsert ;
}
}
spin_unlock ( & jffs2_compressor_list_lock ) ;
printk ( KERN_WARNING " JFFS2: compressor %s not found. \n " , name ) ;
return 1 ;
reinsert :
/* list is sorted in the order of priority, so if
we change it we have to reinsert it into the
good place */
list_del ( & comp - > list ) ;
list_for_each_entry ( this , & jffs2_compressor_list , list ) {
if ( this - > priority < comp - > priority ) {
list_add ( & comp - > list , this - > list . prev ) ;
spin_unlock ( & jffs2_compressor_list_lock ) ;
return 0 ;
}
}
list_add_tail ( & comp - > list , & jffs2_compressor_list ) ;
spin_unlock ( & jffs2_compressor_list_lock ) ;
return 0 ;
}
# endif
void jffs2_free_comprbuf ( unsigned char * comprbuf , unsigned char * orig )
{
if ( orig ! = comprbuf )
kfree ( comprbuf ) ;
}
int jffs2_compressors_init ( void )
{
/* Registering compressors */
# ifdef CONFIG_JFFS2_ZLIB
jffs2_zlib_init ( ) ;
# endif
# ifdef CONFIG_JFFS2_RTIME
jffs2_rtime_init ( ) ;
# endif
# ifdef CONFIG_JFFS2_RUBIN
jffs2_rubinmips_init ( ) ;
jffs2_dynrubin_init ( ) ;
# endif
/* Setting default compression mode */
# ifdef CONFIG_JFFS2_CMODE_NONE
jffs2_compression_mode = JFFS2_COMPR_MODE_NONE ;
D1 ( printk ( KERN_INFO " JFFS2: default compression mode: none \n " ) ; )
# else
# ifdef CONFIG_JFFS2_CMODE_SIZE
jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE ;
D1 ( printk ( KERN_INFO " JFFS2: default compression mode: size \n " ) ; )
# else
D1 ( printk ( KERN_INFO " JFFS2: default compression mode: priority \n " ) ; )
# endif
# endif
return 0 ;
}
int jffs2_compressors_exit ( void )
{
/* Unregistering compressors */
# ifdef CONFIG_JFFS2_RUBIN
jffs2_dynrubin_exit ( ) ;
jffs2_rubinmips_exit ( ) ;
# endif
# ifdef CONFIG_JFFS2_RTIME
jffs2_rtime_exit ( ) ;
# endif
# ifdef CONFIG_JFFS2_ZLIB
jffs2_zlib_exit ( ) ;
# endif
return 0 ;
}