Part Number:CC1352R
Hello,
We are using the CC1352R chip in our product with an external SPI flash. We are attempting to use the provided SPIFFS implementation in SimpleLink CC13x2 SDK v2.30.00.45 and are running into some issues.
Our application has three tasks which all need access to a single SPI "spiffs" object/struct. We are using the packaged config file spiffs_config.h from source/third_party/spiffs/spiffs_config.h which defines the multi-task locking functions as SPIFFSNVS_lock and SPIFFSNVS_unlock which are implemented in SPIFFSNVS.c
The issue we run into is a deadlock in the SPIFFSNVS_lock when it calls into MutexP_lock - this happens when a file is opened from the 1st task, written to from the 2nd task, closed from the 3rd task, and then the deadlock occurs when accessing the spiffs object from the 1st task again - e.g. when trying to do a directory listing
Debugging the problem I can see that there are two issues and I'm not sure where to file a bug report for these? I'm posting in the case that it helps other developers and may hopefully be fixed in the next SDK revision.
Bug 1:
The makefile simplelink_cc13x2_sdk_2_30_00_45/source/third_party/spiffs/lib/ccs/m4f/makefile defines the C preprocessor directive CC13XX_CC26XX_WRITE_LIMIT which is used in spiffs_hydrogen.c to alter spiffs_write to work around some internal flash limitation. If defined the original implementation gets redefined as SPIFFS_write_delegate and a new SPIFFS_write function is defined as a wrapper around this.
SPIFFS_write in this circumstance is missing a call to SPIFFS_UNLOCK(fs); at the end of the function - it is locked on line 670 but needs an unlock at the end of the function (I have added this at line 704). Note that a recursive lock will be taken when calling into the real SPIFFS_write_delegate function which will expose bug 2 (and note that the SPIFFSNVS_Lock struct defined in SPIFFSNVS.h allows for a max of two recursive locks)
[spiffs_hydrogen.c line 658]
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len);
#if SPIFFS_READ_ONLY
(void)fs; (void)fh; (void)buf; (void)len;
return SPIFFS_ERR_RO_NOT_IMPL;
#else
spiffs_stat stats;
s32_t res;
s8_t buffer;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs);
/*
* To comply with the 83 write limit, the current logical page must be
* relocated if we are attempting to write < 4 bytes to the end of the file.
*/
if (len < 4) {
res = SPIFFS_fstat(fs, fh, &stats);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = SPIFFS_eof(fs, fh);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
if (res && stats.size != 0) {
/*
* To relocate the page we simply read the last byte in the file &
* re-write it. SPIFFS will treat this as a modification and will
* automatically copy content to a new page.
*/
res = SPIFFS_lseek(fs, fh, -1, SPIFFS_SEEK_CUR);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = SPIFFS_read(fs, fh, &buffer, 1);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = SPIFFS_lseek(fs, fh, -1, SPIFFS_SEEK_END);
res = SPIFFS_write_delegate(fs, fh, (void *) &buffer, 1);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
}
}
res = SPIFFS_write_delegate(fs, fh, buf, len);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
SPIFFS_UNLOCK(fs); // <----- THIS UNLOCK WAS MISSING
return (res);
#endif
}
#endif
Note: SPIFFS_API_CHECK_RES_UNLOCK is defined as:
#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \
if ((res) < SPIFFS_OK) { \
(fs)->err_code = (res); \
SPIFFS_UNLOCK(fs); \
return (res); \
}
Bug 2:
The implementation of SPIFFSNVS_unlock in SPIFFSNVS.c has an error that unlocks the wrong key slot. The original implementation is:
/*
* ======== SPIFFSNVS_lock ========
*/
void SPIFFSNVS_lock(void *fs)
{
SPIFFSNVS_Lock *lock;
lock = &(((SPIFFSNVS_Data *) ((spiffs *) fs)->user_data)->lock);
lock->keys[lock->count++] = MutexP_lock(lock->mutex);
}
/*
* ======== SPIFFSNVS_lock ========
*/
void SPIFFSNVS_unlock(void *fs)
{
SPIFFSNVS_Lock *lock;
lock = &(((SPIFFSNVS_Data *) ((spiffs *)fs)->user_data)->lock);
lock->count--;
MutexP_unlock(lock->mutex, lock->keys[lock->count + 1]); // <----------- WRONG KEY UNLOCKED
}
The issue that that the wrong slot is passed to MutexP_unlock. This often goes un-noticed in single-task apps as the structure is 0 initialised and MutexP_lock can return "0" as a valid key the first time it is called.
The corrected implementation is:
/*
* ======== SPIFFSNVS_lock ========
*/
void SPIFFSNVS_lock(void *fs)
{
SPIFFSNVS_Lock *lock;
lock = &(((SPIFFSNVS_Data *) ((spiffs *) fs)->user_data)->lock);
lock->keys[lock->count++] = MutexP_lock(lock->mutex);
}
/*
* ======== SPIFFSNVS_lock ========
*/
void SPIFFSNVS_unlock(void *fs)
{
SPIFFSNVS_Lock *lock;
lock = &(((SPIFFSNVS_Data *) ((spiffs *)fs)->user_data)->lock);
lock->count--;
MutexP_unlock(lock->mutex, lock->keys[lock->count]); // <------ UNLOCK THE CORRECT KEY
}
Is anyone else able to replicate this issue with the TI CC13x2 SPIFFS implementation?